Django通过URL传递PK设置表单Select值,__init__执行两次清空值
解决Django Form __init__执行两次导致Select选项被清空的问题
咱们先拆解一下问题根源:你看到__init__执行两次,其实是两个不同的CrearDelitoForm实例被创建:
- 第一次是
CreateView通过get_form方法实例化的单个表单,这时候你通过get_form_kwargs传递了numero_pk,所以imputado的queryset是正确的; - 第二次是你在
get_context_data里实例化DelitoFormset时,内部的每个子表单都会调用__init__,但这里你没传递numero_pk,导致queryset被重置为Imputado.objects.filter(numero_id=None)(也就是空集合),所以看起来选项被清空了。
下面是具体的修复步骤和代码:
1. 优化Form的__init__方法
先给表单的__init__加个判断,避免无numero_pk时覆盖默认queryset:
class CrearDelitoForm(forms.ModelForm): class Meta: model = Delito exclude = () def __init__(self, numero_pk=None, *args, **kwargs): super().__init__(*args, **kwargs) # 仅当传递了numero_pk时才修改queryset if numero_pk is not None: self.fields["imputado"].queryset = Imputado.objects.filter(numero_id=numero_pk)
2. 修正View的get_context_data方法
关键是给formset传递form_kwargs,让内部每个子表单都能拿到numero_pk,同时还要关联正确的Expediente实例(因为是inlineformset,必须绑定父模型实例):
from django.http import Http404 class CrearDelito(CreateView): model = Delito form_class = CrearDelitoForm template_name = 'crear_delito.html' def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['numero_pk'] = self.kwargs['pk'] return kwargs def get_context_data(self,**kwargs): context = super().get_context_data(**kwargs) # 获取对应的Expediente实例(用get而不是filter,确保是单个对象) try: expediente = Expediente.objects.get(id=self.kwargs['pk']) except Expediente.DoesNotExist: raise Http404("Expediente no encontrado") # 准备formset的参数,核心是传递form_kwargs给子表单 formset_kwargs = { 'instance': expediente, 'form_kwargs': {'numero_pk': self.kwargs['pk']} } # 如果是POST请求,别忘了传递表单数据和文件 if self.request.method == 'POST': formset_kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES }) context['formset'] = DelitoFormset(**formset_kwargs) context['expediente'] = expediente return context
3. 可选:处理Formset的保存(如果需要)
如果你的页面需要同时保存单个表单和formset的数据,还要重写form_valid方法:
def form_valid(self, form): # 先保存单个表单的对象 self.object = form.save() # 再验证并保存formset context = self.get_context_data() formset = context['formset'] if formset.is_valid(): formset.save() return super().form_valid(form)
这样修改后,formset里的每个子表单都会拿到正确的numero_pk,不会再出现queryset被清空的情况,__init__虽然还是会执行多次,但每次都是带着正确参数的有效实例化。
内容的提问来源于stack exchange,提问作者Daniel Azcar
相关产品推荐
相关产品推荐

