Nothing here
Nothing here

Reputation: 2393

How to change ordering after updating get_queryset in Django ListView?

I have a list view in which I implemented a search bar feature but it seems that my ordering is now not working. How do I fix this? I would like to be able to order the list the way I want after the get_queryset method..

class MemoListView(LoginRequiredMixin, ListView):
    model = Memo
    template_name = 'memos/memos.html'
    context_object_name = 'memos'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['memo_list'] = Memo.objects.all()
        return context

    def get_ordering(self):
        ordering = self.request.GET.get('ordering', '-date_time')
        return ordering

    def get_queryset(self):
        query = self.request.GET.get('q')
        if query:
            memo_list = Memo.objects.filter(
                Q(title__icontains=query) | Q(content__icontains=query))
        else:
            memo_list = Memo.objects.all()
        return memo_list

Upvotes: 3

Views: 2704

Answers (3)

Sayyor Y
Sayyor Y

Reputation: 1314

Alternatively, you can just add order_by method to the line where you are making the query. So the return line of the class will change to:

return memo_list.order_by('-date_time')

Upvotes: 1

Pengpeng Hou
Pengpeng Hou

Reputation: 1

you need to call the super get_queryset function to get the get_ordering funtion effective.

add this in your get_queryset() function

queryset = super(IndexView, self).get_queryset()

Upvotes: 0

Mehran
Mehran

Reputation: 1304

If you take a look at the source code of ListView you'd see a class like this.

class MultipleObjectMixin(ContextMixin):
    """A mixin for views manipulating multiple objects."""
    allow_empty = True
    queryset = None
    model = None
    paginate_by = None
    paginate_orphans = 0
    context_object_name = None
    paginator_class = Paginator
    page_kwarg = 'page'
    ordering = None

    def get_queryset(self):
        """
        Return the list of items for this view.

        The return value must be an iterable and may be an instance of
    `    QuerySet` in which case `QuerySet` specific behavior will be enabled.
        """
        if self.queryset is not None:
            queryset = self.queryset
            if isinstance(queryset, QuerySet):
                queryset = queryset.all()
        elif self.model is not None:
            queryset = self.model._default_manager.all()
        else:
                raise ImproperlyConfigured(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset, or override "
                "%(cls)s.get_queryset()." % {
                    'cls': self.__class__.__name__
                }
            )
        ordering = self.get_ordering()
        if ordering:
            if isinstance(ordering, str):
                ordering = (ordering,)
            queryset = queryset.order_by(*ordering)

        return queryset

    def get_ordering(self):
        """Return the field or fields to use for ordering the queryset."""
        return self.ordering

In your code, you are overriding get_ordering() to provide the ordering you want. But you are also overriding the get_queryset() method where the get_ordering() was called. If you did not override get_queryset() method your ordering would have been accepted by the default get_queryset() method. But since you're overriding the get_queryset() method you have use the get_ordering() method explicitly.

Change your get_queryset() by doing something like this.

def get_queryset(self):
    query = self.request.GET.get('q')
    if query:
        memo_list = Memo.objects.filter(
            Q(title__icontains=query) | Q(content__icontains=query))
    else:
        memo_list = Memo.objects.all()
    ordering = self.get_ordering()
    memo_list = memo_list.order_by(ordering)
    # The following checks are not needed since your get_ordering() will always only return ONE string
    # if ordering and isinstance(ordering, str):
        # ordering = (ordering,)
        # memo_list = memo_list.order_by(*ordering)
    return memo_list

Upvotes: 5

Related Questions