Django multi-table-inheritance + django-model-utils query optimization

I have some troubles to optimize ManyToManyField in child model with concrete inheritance.

Assume I have models:

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)

In template I make a call:

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

{% endfor %}

And this performs extra query for each instance. In my view I've tried to optimize in this way :

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

However I'm getting error:

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

As I understand select_subclasses() "converts" parent class into child class. Both child classes have same field but still getting error. What I'm missing?

So, finally, after bunch of experiments and code review I found a simple solution:


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

And voila no more extra/similart queries related to queryset_to_optimize call.
In template I would perform for loop in without child call but directly acces to prefetched objects:

{% 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 %}

Result: query count dropped from 1000+ to 13 queries with 300+ objects on page and each of objects has multiple relations including M2M and FK involving Multi-Table Inherited models.
P.S. Honestly, I did't inspect django-model-utils deeply and, particularly, select_subclasses() method to say exactly how it manages such joins.

Back to Top