You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用inlineformset_factory(及crispy)时SQL查询过多/渲染缓慢

嘿,这个问题我太熟悉了——Django inline formset的N+1查询坑!咱们一步步来解决它:

为什么会出现大量重复SQL查询?

本质是两个核心原因:

  1. 每个BookForm渲染现有书籍时,都会单独查询一次关联的Publisher(默认情况下Django不会自动预取关联对象);
  2. 每个表单的Publisher外键选择框,默认都会重复执行Publisher.objects.all()来获取选项列表。

解决方案1:给Formset的QuerySet加上预取

在你的作者更新视图里,创建formset的时候,一定要用select_related预取Book关联的Publisher。这样所有书籍的出版社信息会一次性拉取完成,不用每个书单独发起查询:

def author_update(request, pk):
    author = get_object_or_404(Author, pk=pk)
    BookFormSet = inlineformset_factory(Author, Book, form=BookForm, extra=1)
    
    if request.method == 'POST':
        # 重点:给queryset加上select_related('publisher')
        formset = BookFormSet(
            request.POST, 
            instance=author, 
            queryset=author.books.all().select_related('publisher')
        )
        # 后续提交逻辑...
    else:
        formset = BookFormSet(
            instance=author, 
            queryset=author.books.all().select_related('publisher')
        )
    
    # 渲染模板...

解决方案2:缓存Publisher选择框的查询结果

如果你的BookForm里用了ModelChoiceField来选择出版社,默认每个表单都会重新查询一遍所有出版社。咱们可以把这个查询结果缓存起来,只执行一次:

在forms.py里修改BookForm:

class BookForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 只查询一次出版社列表,避免重复查询
        if not hasattr(self.fields['publisher'], '_cached_queryset'):
            self.fields['publisher'].queryset = Publisher.objects.all()
            self.fields['publisher']._cached_queryset = True

    class Meta:
        model = Book
        fields = ['title', 'publisher']

或者更简洁的方式,提前生成固定选项:

# 在forms.py顶部提前获取一次出版社选项
PUBLISHER_CHOICES = [(p.pk, str(p)) for p in Publisher.objects.all()]

class BookForm(forms.ModelForm):
    publisher = forms.ChoiceField(choices=PUBLISHER_CHOICES)

    class Meta:
        model = Book
        fields = ['title', 'publisher']

验证优化效果:用django-debug-toolbar

建议安装django-debug-toolbar,它能直观展示页面触发的所有SQL查询,帮你确认重复查询是不是真的消失了:

pip install django-debug-toolbar

按照官方文档配置好后,刷新页面就能看到查询数量的明显下降,优化效果一目了然。

小提醒:crispy-forms的细节

因为你用了{% crispy formset formset.form.helper %},要确保你的BookForm的helper没有在初始化时动态生成选项——尽量把需要查询的内容提前处理好,别让helper触发额外的查询请求。

这样优化后,页面的SQL查询数量会大幅减少,渲染速度肯定能提上来!

内容的提问来源于stack exchange,提问作者nspo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:35:04