如何避免Django Admin中Inline引发的重复查询问题?
解决Django Admin内联外键重复查询问题
实现思路
在A的Admin中一次性预加载所有C模型的选项数据,传递给每个B内联表单,替换外键字段的默认查询逻辑,避免每个内联条目触发单独查询。
具体实现步骤
自定义B的内联表单
重写表单的__init__方法,接收外部传递的C选项数据,替换外键字段的默认choices:from django import forms from .models import B class BInlineForm(forms.ModelForm): class Meta: model = B fields = '__all__' def __init__(self, *args, **kwargs): # 取出父Admin传递的C选项 self.c_choices = kwargs.pop('c_choices', None) super().__init__(*args, **kwargs) # 替换外键字段的选项集 if self.c_choices: self.fields['c'].choices = self.c_choices自定义B的内联类
重写get_formset方法,将父Admin预加载的C选项传递给每个表单:from django.contrib import admin from .models import B class BInline(admin.TabularInline): model = B form = BInlineForm def get_formset(self, request, obj=None, **kwargs): formset = super().get_formset(request, obj, **kwargs) # 从父Admin获取预加载的C选项 c_choices = self.parent_admin.c_choices # 动态给表单绑定选项参数 formset.form = type( formset.form.__name__, (formset.form,), {'__init__': lambda self, *args, **kwargs: super(formset.form, self).__init__(c_choices=c_choices, *args, **kwargs)} ) return formset在A的Admin中预加载C数据
在changeform_view中一次性查询所有C的id和显示文本(用only指定必要字段,减少内存占用),存储到Admin实例属性中:from django.contrib import admin from .models import A, C @admin.register(A) class AAdmin(admin.ModelAdmin): inlines = [BInline] def changeform_view(self, request, object_id=None, form_url='', extra_context=None): # 预加载C的选项,仅查询id和__str__用到的字段 # 替换<str_field>为你C模型__str__方法中用到的实际字段 self.c_choices = [ (c.id, str(c)) for c in C.objects.all().only('id', '<str_field>') ] return super().changeform_view(request, object_id, form_url, extra_context)
注意事项
- 如果C模型数据量极大,一次性加载可能导致内存压力,需根据实际场景权衡。
- 务必用
only指定__str__依赖的字段,避免Django加载完整的C实例,降低内存消耗。 - 若需要过滤C的选项(比如仅显示与当前A关联的C),可调整
C.objects.all()为对应的过滤查询集。
内容的提问来源于stack exchange,提问作者JasonTS
相关产品推荐
相关产品推荐

