Reputation: 2590
I'm currently stuck with applying pagination to my Django view codes. Basically, paginate_by seems automatically to work with queryset
attribute in CBV. But, in my case, I'm not just using queryset
attribute. I'm using filtered queryset
on my own. How can I apply pagination to my codes?
class SearchListView(ListView):
model = Store
template_name = 'boutique/search.html'
paginate_by = 5
def get(self, request, *args, **kwargs):
search_text = request.GET.get('search_text')
sorter = request.GET.get('sorter')
if not sorter:
sorter = 'popularity'
if search_text:
search_stores = Store.objects.filter(Q(businessName__icontains=search_text) | Q(mKey__icontains=search_text))
if sorter == 'businessName':
search_stores = search_stores.order_by(sorter)
else:
search_stores = search_stores.order_by(sorter).reverse()
else:
search_stores = ''
for store in search_stores:
store.mKey = store.mKey.split(' ')
return render(request, self.template_name, {
'search_stores': search_stores,
'search_text': search_text,
'sorter': sorter,
})
-------------------------UPDATE2-------------------------
I'm stuck with getting inputs from HTML input tags. Currently, I just put self.kwargs()
, but I don't know what I should use instead of them.
class SearchListView(ListView):
model = Store
template_name = 'boutique/search.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_text'] = self.kwargs['search_text']
context['sorter'] = self.kwargs['sorter']
return context
def get_queryset(self):
search_text = self.kwargs['search_text']
sorter = self.kwargs['sorter']
if not sorter:
sorter = 'popularity'
if search_text:
search_stores = Store.objects.filter(Q(businessName__icontains=search_text) | Q(mKey__icontains=search_text))
if sorter == 'businessName':
search_stores = search_stores.order_by(sorter)
else:
search_stores = search_stores.order_by(sorter).reverse()
else:
search_stores = ''
for store in search_stores:
store.mKey = store.mKey.split(' ')
return search_stores
Upvotes: 4
Views: 4388
Reputation: 1541
You've overridden the get
method of the ListView
and have not called up the super
chain. As a result, the pagination bits and bob that are implemented in the parent ListView.get()
are not getting called.
Rather place your filtering actions in the get_queryset()
or get_context()
methods (both will have access to self.request
) or make sure to call the parent get()
in your get()
.
Look at the source code. ListView
inherits BaseListView
which implements the pagination stuff.
class BaseListView(MultipleObjectMixin, View):
"""
A base view for displaying a list of objects.
"""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
'class_name': self.__class__.__name__,
})
context = self.get_context_data()
return self.render_to_response(context)
But your code never calls BaseListView.get()
so no pagination is ever done.
More importantly, you need to tell your CBV what to paginate on. See that first line? That is why you need a get_queryset
method which literally returns the queryset that is going to be used for pagination.
Now your context variables get implemented in get_context_data
which is provided by your parent hierarchy. Here you build up the context data that will be used in your templates.
Upvotes: 2
Reputation: 23281
I strongly advocate for using this phantasmic library for filtering: https://github.com/carltongibson/django-filter
for the purpose of filtering you define FilterSet with specifying model and allowed fields for filtering
import django_filters
class SpotFilterSet(django_filters.FilterSet):
class Meta:
model = Spot
fields = [
'is_certificated',
'name',
'location',
'spot_type',
'tags',
]
then in the view you provide filterset_class
and override BaseFilterView
from django_filters.views import BaseFilterView
class BaseSpotListView(BaseFilterView, ListView):
template_name = 'www/spot_list.html'
model = Spot
paginate_by = 6
context_object_name = 'spots'
filterset_class = SpotFilterSet
Upvotes: 3