Многотабличное наследование в Django + оптимизация запросов с помощью django-model-utils

У меня есть некоторые проблемы с оптимизацией ManyToManyField в дочерней модели с конкретным наследованием.

Предположим, что у меня есть модели:

class CoreModel(models.Model):
    parent = ForeignKey(Parent)

class Parent(models.Model):
    pass

    @cached_property
    def child(self):
        return Parent.objects.get_subclass(pk=self.pk)

class ChildA(Parent):
    many = ManyToManyField(SomeModel)

class ChildB(Parent):
    many = ManyToManyField(SomeModel)

В шаблоне я делаю вызов:

{% for m in parent.child.many.all %}
    {{ m.title }} 

{% endfor %}

И это выполняет дополнительный запрос для каждого экземпляра. На мой взгляд, я пытался оптимизировать таким образом :

core_instance = CoreModel.objects.prefetch_related(
                                         Prefetch(
                                             "parent",
                                             queryset=Parent.objects.select_subclasses()\
                                                                .prefetch_related("many")
                                         )

Однако я получаю ошибку:

ValueError: Cannot query ChildAObject(some_pk). Must be "ChildB" instance.

Как я понимаю, select_subclasses() «преобразует» родительский класс в дочерний. Оба дочерних класса имеют одинаковые поля, но все равно получаю ошибку. Что я упускаю?

Итак, наконец, после множества экспериментов и проверки кода я нашел простое решение:


queryset_to_optimize = CoreModel.objects.prefetch_related(
                                     Prefetch(
                                         "parent",
                                        queryset=Parent.objects.prefetch_related("many")\
                                                               .select_subclasses()
                                          )
                                      )

И вуаля, больше никаких дополнительных / похожих запросов, связанных с to queryset_to_optimize вызовом.
В шаблоне я бы выполнил for loop in без дочернего вызова, но напрямую обращался бы к предварительно выбранным объектам:

{% for obj in queryset_to_optimize %}
...some code related to CoreModel...
<!-- Direct access to parent.many objects -->
{% for m in obj.parent.many.objects.all %}
... some code related to many field ...
{% endfor %}
{% endfor %}

Результат: количество запросов уменьшилось с более чем 1000 до 13 запросов с более чем 300 объектами на странице, и каждый из объектов имеет множество связей, включая M2M и FK, включая многостолбцовые унаследованные модели.
P.S. Честно говоря, я не изучал django-model-utils глубоко и, в частности, метод select_subclasses(), чтобы точно сказать, как он управляет такими объединениями.

Вернуться на верх