rust-ssg/blog/posts/2013-08-06_help-i-have-too-many-django-manytomany-queries-fixed.rst

32 lines
1.9 KiB
ReStructuredText
Raw Normal View History

2023-10-14 18:03:36 +00:00
Help, I have too many Django ManyToMany Queries [FIXED]
#######################################################
:date: 2013-08-06 04:00
:author: tyrel
:category: Tech
:tags: python, django, bugs
:slug: help-i-have-too-many-django-manytomany-queries-fixed
:status: published
My boss tasked me with getting the load time of 90 seconds(HOLY CARP!) on one page down. First thing I did was install the Django Debug Toolbar to see what was really happening.
There are currently 2,000 users in the database, the way our model is setup is that a UserProfile can have other UserProfiles attached to it in one of three M2M relations, which in the Django Admin would cause 2,000 queries PER M2M field. This is very expensive as obviously you don't want 10,000 queries that each take 0.3ms to take place.
The solution, after a day and a half of research is to override the **formfield_for_manytomany** method in the Admin class for our UserProfile object.
Our solution is to prefetch for any M2M that are related to the current Model.
.. code-block:: python
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.__class__.__name__ == "ManyToManyField" and \
db_field.rel.to.__name__ == self.model.__name__:
kwargs['queryset'] = db_field.rel.to.objects.prefetch_related("user")
return super(UserProfileInline, self).formfield_for_manytomany(
db_field, request, **kwargs)
This goes inside our admin class **UserProfileInline(admin.StackedInline)**. Simple clean and easy to drop into another ModelAdmin with minimal changes.
Other things I pondered was to set all our M2M's as raw_id_fields, then using Select2 or Chosen, query our UserProfiles when the related users were being selected. This would take a lot of load off the initial page load, but is more of a bandaid rather than a real fix.
I tried to override the Admin class's **def queryset(self, request):** but this was not affecting anything.