126

I recently learned that you should override the get method when you specifically want to do something other than what the default view does:

class ExampleView(generic.ListView):
    template_name = 'ppm/ppm.html'

    def get(self, request):
        manager = request.GET.get('manager', None)
        if manager:
            profiles_set = EmployeeProfile.objects.filter(manager=manager)
        else:
            profiles_set = EmployeeProfile.objects.all()
            context = {
                'profiles_set': profiles_set,
                'title': 'Employee Profiles'
            }

That's simple enough, but when should I use get_queryset or get_context_data over get? To me it seems like they basically do the same thing or am I just missing something? Can I use them together? This is a major source of confusion for me.

So to reiterate: In what cases would I use get over get_queryset or get_context_data and vise versa?

Underoos
  • 4,708
  • 8
  • 42
  • 85
MassDefect_
  • 1,821
  • 3
  • 20
  • 24
  • 4
    On the contrary; you should rarely override `get`, and certainly not in this case. Here you should only be overriding `get_context_data`. – Daniel Roseman Apr 30 '16 at 09:35

2 Answers2

223

They indeed do different things.

get()

This is a top-level method, and there's one for each HTTP verb - get(), post(), patch(), etc. You would override it when you want to do something before a request is processed by the view, or after. But this is only called when a form view is loaded for the first time, not when the form is submitted. Basic example in the documentation. By default it will just render the configured template and return the HTML.

class MyView(TemplateView):
    # ... other methods

    def get(self, *args, **kwargs):
        print('Processing GET request')
        resp = super().get(*args, **kwargs)
        print('Finished processing GET request')
        return resp

get_queryset()

Used by ListViews - it determines the list of objects that you want to display. By default, it will just give you all for the model you specify. By overriding this method you can extend or completely replace this logic. Django documentation on the subject.

class FilteredAuthorView(ListView):
    template_name = 'authors.html'
    model = Author

    def get_queryset(self):
        # original qs
        qs = super().get_queryset() 
        # filter by a variable captured from url, for example
        return qs.filter(name__startswith=self.kwargs['name'])

get_context_data()

This method is used to populate a dictionary to use as the template context. For example, ListViews will populate the result from get_queryset() as author_list in the above example. You will probably be overriding this method most often to add things to display in your templates.

def get_context_data(self, **kwargs):
    data = super().get_context_data(**kwargs)
    data['page_title'] = 'Authors'
    return data

And then in your template, you can reference these variables.

<h1>{{ page_title }}</h1>

<ul>
{% for author in author_list %}
    <li>{{ author.name }}</li>
{% endfor %}
</ul>

Now to answer your main question, the reason you have so many methods is to let you easily stick your custom logic with pin-point accuracy. It not only allows your code to be more readable and modular, but also more testable.

The documentation should explain everything. If still not enough, you may find the sources helpful as well. You'll see how everything is implemented with mixins which are only possible because everything is compartmentalized.

mrghofrani
  • 1,335
  • 2
  • 13
  • 32
Anonymous
  • 11,740
  • 3
  • 40
  • 50
  • 22
    [CCBV](http://ccbv.co.uk/) makes understanding how class-based views are put together even easier than reading the source. – Dustin Wyatt Aug 24 '16 at 19:05
  • will be correct to imply that if you are working with a larga data set, it is better to use get_query_set() to filter the initial result to avoid having such a big list in memory? – Manza Jul 28 '18 at 21:33
  • Probably. You can also [slice in the templates](https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#slice). – Anonymous Jul 29 '18 at 03:10
  • If I am building the context object (e.g. from multiple queries of different models) with `get_context_data()` then why do I still need `get_query_set()`, and how can I get rid of it if I am not using it? – user5359531 Nov 01 '18 at 23:48
  • 1
    Nice explanation thanks. You want to change get_query_set() headline to get_queryset() (without underscore) – Kaan Karaca Dec 18 '18 at 22:01
  • A nice bit of info about get(): "But this is only called when a form view is loaded for the first time." This directly helped me modify a model object which wasn't being reflected in its form until being refreshed by encouraging me to move my update into the class's get() from its get_context_data(). – gregory Jan 30 '19 at 04:51
  • 1
    No `get_query_set()`, `get_queryset()`. – Dalinaum Nov 09 '19 at 06:37
  • Great answer! But is there some kind of convention, where I should do filtering / data processing only in `get_queryset` then build my context only in `get_context_data` and then do some low level HTTP stuff in `get`? Cause I have seen people (myself included) do some data processing in `get_context_data` – ruslaniv Aug 10 '20 at 08:47
16

Let's look at the default implementation of ListView's get method:

https://github.com/django/django/blob/92053acbb9160862c3e743a99ed8ccff8d4f8fd6/django/views/generic/list.py#L158

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)

You will notice that get_queryset gets called in the first line. You can simply overwrite that if you just want to return your model's queryset after applying some filtering/ordering etc.

You don't need to overwrite the whole get method for that because you will be missing on all this provided functionality i.e. pagination, 404 checks etc.

get_context_data merges the resulting queryset together with context data like querystring parameters for pagination etc.

What I would recommend would be to check with django's source every once in a while and try to understand it a little bit so that you can recognize the most appropriate method you can overwrite/replace.

fips
  • 4,319
  • 5
  • 26
  • 42