Когда использовать get, get_queryset, get_context_data в Django?

Недавно я узнал, что вам следует переопределить метод get, когда вы конкретно хотите сделать что-то отличное от того, что делает представление по умолчанию:

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'
            }

Это достаточно просто, но когда я должен использовать get_queryset или get_context_data над get? Мне кажется, что они в основном делают то же самое, или я просто что-то упустил? Могу ли я использовать их вместе? Это главный источник путаницы для меня.

Так повторить: В каких случаях я использовал бы получить более get_queryset или get_context_data и наоборот?

Ответы

Ответ 1

Они действительно делают разные вещи.

get()

Это метод верхнего уровня, и один для каждого глагола HTTP - get(), post(), patch(), и т.д. Вы бы переопределить его, когда вы хотите сделать что - то до того, как запрос обрабатывается мнение, или после. Но это вызывается только тогда, когда представление формы загружается впервые, а не при отправке формы. Простой пример в документации. По умолчанию он просто отображает настроенный шаблон и возвращает 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()

Используется ListView - определяет список объектов, которые вы хотите отобразить. По умолчанию это просто даст вам все для модели, которую вы указали. Переопределив этот метод, вы можете расширить или полностью заменить эту логику. Джанго документация по данному вопросу.

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()

Этот метод используется для заполнения словаря для использования в качестве контекста шаблона. Например, ListView будет заполнить результат с get_queryset() как author_list в приведенном выше примере. Вероятно, вы будете переопределять этот метод чаще всего, чтобы добавлять элементы для отображения в свои шаблоны.

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

И тогда в вашем шаблоне вы можете ссылаться на эти переменные.

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

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

Теперь, чтобы ответить на ваш главный вопрос, причина, по которой у вас так много методов, состоит в том, чтобы позволить вам легко придерживаться своей пользовательской логики с высокой точностью. Это не только позволяет вашему коду быть более читабельным и модульным, но и более тестируемым.

Документация должна объяснить все. Если все еще не достаточно, вы можете найти источники полезной. Вы увидите, как все реализовано с помощью миксинов, которые возможны только потому, что все разделено.

Ответ 2

Давайте посмотрим на стандартную реализацию метода get ListView:

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 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)

Вы заметите, что get_queryset в первой строке. Вы можете просто перезаписать это, если вы просто хотите вернуть свой набор запросов модели после применения некоторой фильтрации/упорядочения и т.д.

Вам не нужно перезаписывать весь метод get для этого, потому что вам не хватает всей этой предоставляемой функциональности, то есть нумерации страниц, 404 проверок и т.д.

get_context_data объединяет результирующий get_context_data с данными контекста, такими как параметры get_context_data запроса для разбивки на страницы и т.д.

Я бы порекомендовал проверять исходники django время от времени и пытаться понять их немного, чтобы вы могли распознать наиболее подходящий метод, который вы можете перезаписать/заменить.