django: how to make query be not lazy executed?

0

Issue

I have problem with query lazy execution in my custom Manager method. In it i want to separate query by model CharField choices and return dict[choice, QuerySet].

model.py part:

...

PRODUCT_STATUS = [
    ('pn', 'Запланировано'),
    ('ga', 'В процессе (Ознакомляюсь)'),
    ('rv', 'Повтор'),
    ('ac', 'Завершено'),
    ('ab', 'Брошено'),
    ('pp', 'Отложено'),
]


class ExtendedManager(models.Manager):
    def separated_by_status(self, product_type):
        query = super().get_queryset().all()
        dict_ = {}
        for status in PRODUCT_STATUS:
            dict_.update({status[1]: query.filter(status=status[0]).all()})
        return dict_

...

views.py part with manager use:

...

class ProductListView(ContextMixin, View):
    template_name = 'myList/myList.html'

    def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        product = kwargs.get('product')
        if product not in {'film', 'game', 'series', 'book'}:
            raise Http404
        context = self.get_context_data()
        context['title'] = f'Список {product}'
        context['dict_queryset'] = Product.objects.separated_by_status(product)
        
        return render(request, self.template_name, context)

...

django debug toolbar result:
here

The problem is that the query is lazy executed in the manager, but in templates it is already fully executed for each PRODUCT_STATUS element separately. How cat it be optimized to execute 1 time?

I’m very sorry if I use the term "lazy" incorrectly.

Solution

The problem is not the laziness, the problem is that you make a QuerySet per PRODUCT_STATUS.

You can make such dictionary with a single pass with the groupby function [Python-doc] of the itertools module [Python-doc]:

from itertools import groupby
from operator import attrgetter

class ExtendedManager(models.Manager):
    def separated_by_status(self, product_type):
        query = super().get_queryset().order_by('status')
        dict_ = {
            k: list(vs)
            for k, vs in groupby(query, attrgetter('status'))
        }
        return {
            status1: dict_.get(status0, [])
            for status0, status1 in PRODUCT_STATUS
        }

Answered By – Willem Van Onsem

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More