Django CBVs: повторное использование запроса из get_queryset в методе get_context_data в ListView

Мне нужно передать в шаблон объект Tag, но без дополнительного запроса к базе данных в методе get_context_data.
Подскажите, пожалуйста, есть ли более элегантный способ получить значение из метода get_queryset в get_context_data. И если есть более элегантный способ, то корректно ли объявлять собственные поля в представлениях Django

class PostListView(ListView):
    model = Post
    paginate_by = 3
    context_object_name = 'posts'
    template_name = 'blog/post/list.html'

    tag = None

    def get_queryset(self):
        data = super().get_queryset()
        if tag_slug := self.kwargs.get('tag_slug'):
            self.tag = get_object_or_404(Tag, slug=tag_slug)
            data = data.filter(tags__in=[self.tag])
        return data

    def get_context_data(self, *, object_list=None, **kwargs):
        data = super().get_context_data(**kwargs)
        data['tag'] = self.tag
        return data

get queryset должен только подготовить queryset без попадания в базу данных. В вашем случае вы делаете много не нужных вещей:

 def get_queryset(self):
    query =  Q()
    if self.kwargs.get('tag_slug'):
        query = Q(tags__slug=self.kwargs['tag_slug']) 
    return super().get_queryset().filter(query).select_related('tag')

в действительности функция выше - это One-liner. Вам не нужен никакой тег в контексте, но если вы хотите:

def get_context_data(self, *args, **kwargs):
    response = super().get_context_data(*args, **kwargs)
    if len(response['object_list']) : # only one hit in database
        response['tag'] = response['object_list'][0].tag
    else:
        raise Http404
    return response

len сделал запрос в базу данных, и получил все объекты.

На основании ответа я отредактировал свой код следующим образом:

  • поскольку мне нужно имя тега и оно может отличаться от tug slug, я переопределяю метод get, чтобы получить объект тега и не выполнять код uselees позже

  • удален запрос из get_queryset

  • заменил "object_list" на "posts" из-за итерации через посты в моем шаблоне

    class PostListView(ListView):
     model = Post
     paginate_by = 3
     context_object_name = 'posts'
     template_name = 'blog/post/list.html'
    
     tag = None
    
     def get(self, request, *args, **kwargs):
         if tag_slug := self.kwargs.get('tag_slug'):
             self.tag = get_object_or_404(Tag, slug=tag_slug)
         return super(PostListView, self).get(request, *args, **kwargs)
    
     def get_queryset(self):
         query = Q()
         if self.tag:
             query = Q(tags__slug=self.tag)
         return super(PostListView, self).get_queryset().filter(query)
    
     def get_context_data(self, *args, **kwargs):
         response = super().get_context_data(*args, **kwargs)
    
         if len(response['posts']):
             response['tag'] = self.tag
         else:
             raise Http404
         return response
    
Вернуться на верх