如何强制 Django Admin 使用 select_related?
Posted
技术标签:
【中文标题】如何强制 Django Admin 使用 select_related?【英文标题】:How to force Django Admin to use select_related? 【发布时间】:2011-10-17 02:23:48 【问题描述】:我的一个模型特别复杂。当我尝试在 Django Admin 中对其进行编辑时,它会执行 1042 次查询并需要 9 秒以上的时间来处理。
我知道我可以用raw_id_fields
替换一些下拉菜单,但我认为更大的瓶颈是它没有按照应有的方式执行select_related()
。
我可以让管理站点执行此操作吗?
【问题讨论】:
【参考方案1】:虽然 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')
【讨论】:
查询集已重命名为get_queryset 这在 change 页面上似乎不起作用。即,表单仍在进行数千次查询以获取相关实例。只是我,还是这对其他人有用? django==1.11.3 它给出了这个错误:super(type, obj): obj must be an instance or subtype of type【参考方案2】:你可以试试这个
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
【讨论】:
不幸的是,我认为这不能解决原始查询。list_select_related
属性用于管理 listing 页面,而不是对象 edit 页面。当然,根据我遇到同样问题的经验,设置列表相关设置不会加快编辑页面,只有列表和选择页面。【参考方案3】:
对于我的特定模型,特别慢的方面是当 ForeignKeys 显示在表单中时,它们不是使用 select_related
调用的,所以这就是我要加快速度的部分。
查看相关的django源码,你在django/contrib/admin/options.py
看到方法formfield_for_foreignkeys
接受每个FK db_field
并调用ForeignKey
类的formfield
方法,该方法定义在django/db/models/领域/相关/喜欢:
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)
由此,我们看看是否为db_field
提供kwargs['queryset']
,我们可以定义一个将使用select_related 的自定义查询集(这可以由formfield_for_foreignkey
提供)。
所以基本上我们想要做的就是用SelectRelatedModelAdmin
覆盖admin.ModelAdmin
,然后将我们的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)
此代码示例不包括 admin Inline
s 或 ManyToManyField
s,或由 readonly_fields
或自定义 select_related 查询调用的函数中的 foreign_key 遍历,但类似的方法应该适用于这些情况。
【讨论】:
这可以通过get_field_queryset
来简化(尽管它没有记录,因此将来可能会更改)。【参考方案4】:
在 Django 2.0+ 中,提高 ForeignKey 和 ManyToMany 关系性能的一个好方法是使用autocomplete fields。
这些字段不会显示所有相关对象,因此加载的查询要少得多。
【讨论】:
【参考方案5】:对于管理员编辑/更改特定项目页面,外键选择框可能需要很长时间才能加载,以改变 django 查询外键数据的方式:
Django docs on Using formfield_for_foreignkey
假设我的Example
模型上有一个名为foo
的字段,我希望选择相关的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)
【讨论】:
【参考方案6】:为了完整起见,我想添加另一个最适合我的用例的选项。
正如其他人所指出的,问题通常是为选择框加载数据。 list_select_related
在这种情况下没有帮助。
如果您实际上不想通过管理员编辑外键字段,最简单的解决方法是将相应字段设为只读:
class Foo(admin.ModelAdmin):
readonly_fields = ('foreign_key_field1','foreign_key_field2',)
您仍然可以显示这些字段,根本不会有选择框,因此 Django 不需要从数据库中检索所有选择框选项。
【讨论】:
以上是关于如何强制 Django Admin 使用 select_related?的主要内容,如果未能解决你的问题,请参考以下文章
如果项目文件夹已经存在,则强制 django-admin startproject
强制转换为 Unicode:需要字符串或缓冲区,在 django admin 中渲染时发现 NoneType
Django Admin Cookbook-18如何限制对Django Admin管理部分功能的使用