Django Form用queryset=Product.objects.none()报Select a valid choice错误如何解决
问题原因
Django 的 ModelChoiceField 在校验提交数据时,会默认验证提交的选项是否属于字段初始化时指定的 queryset 范围。你将 product 字段的初始 queryset 设为 Product.objects.none(),提交时没有符合范围的可选值,因此触发了 Select a valid choice 的校验错误。
解决方案
根据你的业务场景可以选择以下两种方案:
方案1:动态关联分类筛选商品(推荐)
你的前端已经实现了「选分类后联动加载对应分类商品」的逻辑,可以在表单初始化时判断是否为提交场景,动态加载对应分类下的商品作为 product 字段的校验范围,既保证校验逻辑严谨,又不会全量加载商品:
class OrderProductsForm(forms.ModelForm): class Meta: model = OrderProducts fields = ['amount'] def __init__(self, *args, **kwargs): super(OrderProductsForm, self).__init__(*args, **kwargs) self.fields['product_category'] = ModelChoiceField( queryset=ProductCategory.objects.all(), empty_label="Ürün Grubunu seciniz", widget=forms.Select(attrs={"onChange":'myFunction(this.value,this.id)'}) ) # 初始默认空查询集,渲染页面时不加载全量商品 product_queryset = Product.objects.none() # 如果是POST提交的绑定数据场景,动态加载对应分类下的商品 if self.is_bound: category_id = self['product_category'].value() if category_id and category_id.isdigit(): product_queryset = Product.objects.filter(category_id=category_id) self.fields['product'] = ModelChoiceField( queryset=product_queryset, empty_label="Ürün Seciniz", required=False, widget=forms.Select(attrs={"onChange":'stokKontrol(this.value,this.id)'}) ) self.fields['stok'] = forms.CharField(required=False,disabled=True,max_length=5)
方案2:重写字段校验逻辑
如果不需要严格校验商品和分类的从属关系,只需要保证提交的商品ID合法存在,可以重写 product 字段的清洗方法,跳过默认的 queryset 校验:
class OrderProductsForm(forms.ModelForm): class Meta: model = OrderProducts fields = ['amount', 'product'] # 注意要把product加入fields def __init__(self, *args, **kwargs): super(OrderProductsForm, self).__init__(*args, **kwargs) self.fields['product_category'] = ModelChoiceField( queryset=ProductCategory.objects.all(), empty_label="Ürün Grubunu seciniz", widget=forms.Select(attrs={"onChange":'myFunction(this.value,this.id)'}) ) self.fields['product'] = ModelChoiceField( queryset=Product.objects.none(), empty_label="Ürün Seciniz", required=False, widget=forms.Select(attrs={"onChange":'stokKontrol(this.value,this.id)'}) ) self.fields['stok'] = forms.CharField(required=False,disabled=True,max_length=5) def clean_product(self): product_id = self.cleaned_data.get('product') if not product_id: return None try: return Product.objects.get(id=product_id) except Product.DoesNotExist: raise forms.ValidationError("所选商品不存在")
两种方案修改后,原视图层的 formset.is_valid() 逻辑不需要做任何改动,即可正常校验。
内容的提问来源于stack exchange,提问作者NAZIM AVCI
相关产品推荐
相关产品推荐

