djangoviewdetailview

How I can use pagination for my Class (DetailView) with get_context_data?


How can I get pagination for the current code? I can't change the DetailView to View or ListView, so in this class I get a filter of products.

class CategoryDetailView(DetailView):
    model = Category
    queryset = Category.objects.all()
    context_object_name = 'category'
    template_name = 'category_products.html'
    ...


def get_context_data(self, **kwargs):

    context = super().get_context_data(**kwargs)
    ...
    products = Product.objects.filter(id__in=[pf_['product_id'] for pf_ in pf])
    context['category_products'] = products
    return context

Solution

  • Typically one picks the class-based view that is the most convenient to do the job. You can see your view as a ListView of Products that are related to a certain category. By doing this, you make it more convenient to retrieve the related Products, and less convenient to obtain the relevant Category, but the last one is easier.

    We can implement the view as:

    from django.shortcuts import get_object_or_404
    
    class CategoryDetailView(ListView):
        model = Product
        context_object_name = 'category_products'
        template_name = 'category_products.html'
        paginate_by = 1
        # …
    
        def get_queryset(self):
            qs = super().get_queryset().filter(
                category__slug=self.kwargs['slug']
            )
            qd = self.request.GET.copy()
            qd.pop(self.page_kwarg, None)
            if 'search' in self.request.GET:
                qs = qs.filter(title__icontains=self.request.GET['search'])
            elif qd:
                qs = qs.filter(
                    features__value__in=[v for _, vs in qd.lists() for v in vs]
            )
            if 'sort' in self.request.GET:
                qs = qs.order_by(self.request.GET['sort'])
            return qs
    
        def get_context_data(self, *args, **kwargs):
            context = super().get_context_data(*args, **kwargs)
            context['category'] = get_object_or_404(Category, pk=self.kwargs['pk'], slug=self.kwargs['slug'])
            return context

    The get_queryset here thus implements the advanced querying you implemented, and we add the category by overriding the get_queryset. This queryset will then automatically get paginated by the logic of the ListView.

    I would however advise to simplify the logic, usually advanced filtering only makes it more error prone. Furthermore ordering by an arbitrary parameter introduces a security vulnerability, one could for example try to filter on some_foreignkey__some_secure_field to expose data. You thus might want to validate that the order is in a list of acceptable values.