How to force Django Admin to use select_related?

≡放荡痞女 提交于 2019-11-28 20:07:25

问题


One of my models is particularily complex. When I try to edit it in Django Admin it performs 1042 queries and takes over 9 seconds to process.

I know I can replace a few of the drop-downs with raw_id_fields, but I think the bigger bottleneck is that it's not performing a select_related() as it should.

Can I get the admin site to do this?


回答1:


Although dr jimbob's answer makes sense, for my needs, I was able to simply override the get_queryset() method with a one-liner, even selecting a foreign key's foreign key. Maybe this could be helpful to someone.

class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    ...
    def get_queryset(self, request):
        return super(MyModelAdmin, self).get_queryset(request).select_related(
            'foreign_key1', 'foreign_key2__fk2_foreign_key')



回答2:


you can try this

class Foo(admin.ModelAdmin):
    list_select_related = (
        'foreign_key1',
        'foreign_key2',
    )

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_select_related




回答3:


For my particular model, the particularly slow aspect is going through ForeignKeys when they were being displayed in forms, which aren't called using select_related, so that's the part I'm going to speed up.

Looking through the relevant django source, you see in django/contrib/admin/options.py that the method formfield_for_foreignkeys takes each FK db_field and calls the ForeignKey class's formfield method, which is defined in django/db/models/fields/related/ like:

def formfield(self, **kwargs):
    db = kwargs.pop('using', None)
    defaults = {
        'form_class': forms.ModelChoiceField,
        'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to),
        'to_field_name': self.rel.field_name,
    }
    defaults.update(kwargs)
    return super(ForeignKey, self).formfield(**defaults)

From this, we see if we provide the db_field with a kwargs['queryset'] we can define a custom queryset that will be use select_related (this can be provided by formfield_for_foreignkey).

So basically what we want to do is override admin.ModelAdmin with SelectRelatedModelAdmin and then make our ModelAdmin subclasses of SelectRelatedModelAdmin instead of admin.ModelAdmin

class SelectRelatedModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if 'queryset' in kwargs:
            kwargs['queryset'] = kwargs['queryset'].select_related()
        else:
            db = kwargs.pop('using', None)
            kwargs['queryset'] = db_field.rel.to._default_manager.using(db).complex_filter(db_field.rel.limit_choices_to).select_related()
        return super(SelectRelatedModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

This code sample doesn't cover admin Inlines or ManyToManyFields, or foreign_key traversal in functions called by readonly_fields or custom select_related queries, but a similar approach should work for those cases.




回答4:


In Django 2.0+, a good way to improve performance of ForeignKey and ManyToMany relationships is to use autocomplete fields.

These fields don't show all related objects and therefore load with many fewer queries.



来源:https://stackoverflow.com/questions/6892906/how-to-force-django-admin-to-use-select-related

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!