How to force Django Admin to use select_related?

前端 未结 5 1485
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-24 06:52

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

相关标签:
5条回答
  • 2020-12-24 07:01

    For the admin edit/change a specific item page, foreign key select boxes may take a long time to load, to alter the way django queries the data for the foreign key:

    Django docs on Using formfield_for_foreignkey

    Say I have a field called foo on my Example model, and I wish to select ralated bar objects:

    class ExampleAdmin(admin.ModelAdmin):
    
        def formfield_for_foreignkey(self, db_field, request, **kwargs):
                if db_field.name == "foo":
                    kwargs["queryset"] = MachineTool.objects.select_related('bar')
                return super().formfield_for_foreignkey(db_field, request, **kwargs)
    
    0 讨论(0)
  • 2020-12-24 07:08

    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.

    0 讨论(0)
  • 2020-12-24 07:09

    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

    0 讨论(0)
  • 2020-12-24 07:16

    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')
    
    0 讨论(0)
  • 2020-12-24 07:17

    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.

    0 讨论(0)
提交回复
热议问题