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

Django通过URL传递PK设置表单Select值,__init__执行两次清空值

解决Django Form __init__执行两次导致Select选项被清空的问题

咱们先拆解一下问题根源:你看到__init__执行两次,其实是两个不同的CrearDelitoForm实例被创建:

  1. 第一次是CreateView通过get_form方法实例化的单个表单,这时候你通过get_form_kwargs传递了numero_pk,所以imputado的queryset是正确的;
  2. 第二次是你在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:47:52