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
相关产品推荐
相关产品推荐

