Многотабличное наследование в 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()
, чтобы точно сказать, как он управляет такими объединениями.