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

Django中ModelForm的__init__频繁调用的优化及选项填充问题

嘿,这个问题我之前踩过坑,django表单的__init__被频繁调用确实挺闹心的,尤其是里面还嵌套数据库查询,那简直是性能杀手。咱们来一步步拆解解决:

先搞懂为啥__init__被调用这么多次

Django(包括django-vanilla-views)在处理表单时,可能会多次实例化表单对象——比如验证用户输入前后、模板渲染时的字段遍历、甚至某些视图内部的逻辑判断,都可能触发表单初始化。如果你的__init__里每次都查DB,那18次调用就意味着18次重复查询,完全没必要。

解决方案1:给查询结果加缓存(最直接的优化)

既然每次查询的结果大概率是重复的,咱们把查询结果缓存起来,只查一次就够了。有两种缓存方式可选:

方式A:表单类级别的静态缓存

适合数据不经常变动的场景,第一次查询后把结果存在表单类的变量里,所有后续实例复用:

from django import forms
from .models import MyModel, TargetOptionModel

class MyModelModelForm(forms.ModelForm):
    # 先声明字段,暂不设置choices
    my_choice_field = forms.ChoiceField()
    
    # 类级缓存变量,用来存查询后的选项
    _cached_choices = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # 只有缓存为空时才执行DB查询
        if not MyModelModelForm._cached_choices:
            # 这里替换成你的实际查询逻辑
            queryset = TargetOptionModel.objects.all().values_list('id', 'display_name')
            MyModelModelForm._cached_choices = list(queryset)
        
        # 给字段赋值缓存好的选项
        self.fields['my_choice_field'].choices = MyModelModelForm._cached_choices

方式B:用Django官方缓存框架

如果数据会动态更新,需要定期刷新缓存,就用Django自带的缓存:

from django import forms
from django.core.cache import cache
from .models import MyModel, TargetOptionModel

class MyModelModelForm(forms.ModelForm):
    my_choice_field = forms.ChoiceField()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # 从缓存取数据,不存在则查询并存入缓存
        choices = cache.get('my_form_choices_key')
        if not choices:
            queryset = TargetOptionModel.objects.all().values_list('id', 'display_name')
            choices = list(queryset)
            # 设置缓存过期时间,比如1小时(3600秒)
            cache.set('my_form_choices_key', choices, timeout=3600)
        
        self.fields['my_choice_field'].choices = choices

解决方案2:把选项填充移到视图层(更可控)

既然django-vanilla-views是基于类的视图,咱们可以在视图的get_form方法里提前查询一次选项,再传给表单。这样一个请求周期内只会查一次DB,完全避免表单多次初始化带来的重复查询:

from vanilla import CreateView, UpdateView
from .forms import MyModelModelForm
from .models import MyModel, TargetOptionModel

class MyModelCreateView(CreateView):
    form_class = MyModelModelForm
    model = MyModel

    def get_form(self, *args, **kwargs):
        # 先拿到表单实例
        form = super().get_form(*args, **kwargs)
        # 在这里只查询一次选项
        choices = list(TargetOptionModel.objects.all().values_list('id', 'display_name'))
        # 给表单字段赋值选项
        form.fields['my_choice_field'].choices = choices
        return form

# 更新视图同理
class MyModelUpdateView(UpdateView):
    form_class = MyModelModelForm
    model = MyModel

    def get_form(self, *args, **kwargs):
        form = super().get_form(*args, **kwargs)
        choices = list(TargetOptionModel.objects.all().values_list('id', 'display_name'))
        form.fields['my_choice_field'].choices = choices
        return form

额外提醒:如果是动态过滤的选项

如果你需要根据当前请求(比如当前用户、URL参数)来过滤选项,那视图层的方案更合适——你可以在get_form里拿到self.request或者self.kwargs,来动态生成过滤后的queryset,再传给表单,这样既保证了灵活性,又避免了重复查询。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:17:28