如何强制Django Admin使用select_related?

mpe*_*pen 24 django django-models django-orm django-admin

我的一个模型特别复杂.当我尝试在Django Admin中编辑它时,它执行1042个查询并花费超过9秒来处理.

我知道我可以替换掉一些下拉菜单raw_id_fields,但我认为更大的瓶颈在于它没有表现出select_related()应有的效果.

我可以让管理网站这样做吗?

Clo*_*ans 32

虽然jimbob博士的回答是有道理的,但根据我的需要,我能够简单地用一行代替覆盖get_queryset()方法,甚至可以选择外键的外键.也许这对某人有帮助.

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')
Run Code Online (Sandbox Code Playgroud)

  • 查询集已重命名为 [get_queryset](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_queryset) (2认同)
  • 这似乎不适用于**change**页面.即,表单仍在进行数千次查询以获取相关实例.它只是我,还是这对别人有效?Django的== 1.11.3 (2认同)

小智 20

你可以试试这个

class Foo(admin.ModelAdmin):
    list_select_related = (
        'foreign_key1',
        'foreign_key2',
    )
Run Code Online (Sandbox Code Playgroud)

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

  • 不幸的是,我不认为这解决了原始查询.`list_select_related`属性用于Admin _listing_页面,而不是对象_edit_页面.当然,根据我对同一问题的经验,列表相关集不会加快编辑页面,只会加快列表和选择页面. (2认同)

dr *_*bob 11

对于我的特定模型,当它们以表格形式显示时,特别慢的方面是通过ForeignKeys,而不是使用select_related,这是我要加速的部分.

通过查看相关的django源代码,您会看到django/contrib/admin/options.py 该方法formfield_for_foreignkeys采用每个FK db_field并调用ForeignKey类的 formfield方法,该方法在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)
Run Code Online (Sandbox Code Playgroud)

从这里,我们看到我们是否提供了db_field一个kwargs['queryset']我们可以定义一个将使用select_related的自定义查询集(这可以由提供formfield_for_foreignkey).

所以基本上我们想要做的是覆盖admin.ModelAdmin,SelectRelatedModelAdmin然后使用我们的ModelAdmin子类SelectRelatedModelAdmin而不是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)
Run Code Online (Sandbox Code Playgroud)

此代码示例不包括admin InlineManyToManyFields,或者在调用的函数readonly_fields 或自定义select_related查询中的foreign_key遍历,但类似的方法应该适用于这些情况.


run*_*ace 7

对于管理员编辑/更改特定项目页面,外键选择框可能需要很长时间才能加载,以改变 django 查询外键数据的方式:

关于使用 formfield_for_foreignkey 的 Django 文档

foo假设我的模型有一个调用的字段Example,并且我希望选择相关bar对象:

class ExampleAdmin(admin.ModelAdmin):

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == "foo":
                kwargs["queryset"] = Example.objects.select_related('bar')
            return super().formfield_for_foreignkey(db_field, request, **kwargs)
Run Code Online (Sandbox Code Playgroud)


YPC*_*ble 5

在Django 2.0+中,提高ForeignKey和ManyToMany关系性能的一个好方法是使用自动完成字段.

这些字段不显示所有相关对象,因此加载的查询次数更少.