Django中使用jQuery Editable Select时ChoiceField值与标签无法分离的问题
解决Django ChoiceField配合jQuery-Editable-select的提交验证问题
问题原因
jQuery-Editable-select插件在用户搜索选择后,可能将显示的product_denomination文本提交给表单,而非原option对应的id_product值,导致Django的ChoiceField验证时判定提交值不在合法选项列表中,抛出Select a valid choice. That choice is not one of the available choices错误。
解决方案
方案1:修正插件提交行为,确保提交option的value值
初始化插件时,通过onSelect事件同步更新原select元素的value,保证提交的是产品ID而非名称:
$('#sales_documents_editable_select_description_1').editableSelect({ onSelect: function(selectedElement) { // 将原select的value设置为选中项的value(即id_product) $(this).val($(selectedElement).attr('value')); } });
注:部分版本的插件可能会生成模拟输入框,原select被隐藏,此方法可强制同步真实select的value。
方案2:自定义表单字段,适配非标准提交值
如果插件行为难以修改,可自定义ChoiceField,当常规验证失败时,尝试通过提交的产品名称反向匹配对应ID:
from django import forms from .models import models_products class ProductChoiceField(forms.ChoiceField): def validate(self, value): try: # 先执行默认验证逻辑 super().validate(value) except forms.ValidationError: # 根据产品名称查询对应ID product = models_products.objects.filter(product_denomination=value).first() if product: # 将值替换为合法的产品ID self.widget.value = product.id_product return # 无匹配项时抛出原错误 raise forms.ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') # 在表单中使用自定义字段 class forms_sales_documents(forms.ModelForm): sales_documents_description_1 = ProductChoiceField( required=False, choices=list(models_products.objects.values_list('id_product', 'product_denomination')), widget=forms.Select(attrs={ 'id': 'sales_documents_editable_select_description_1', 'style': 'width:200px', 'onchange': 'populate_selected_product(this.id,this.value)' }) )
方案3:改用ModelChoiceField替代ChoiceField
ModelChoiceField专为模型选项设计,默认以模型PK为value,可自定义显示label,更适配此类场景:
class forms_sales_documents(forms.ModelForm): sales_documents_description_1 = forms.ModelChoiceField( required=False, queryset=models_products.objects.all(), widget=forms.Select(attrs={ 'id': 'sales_documents_editable_select_description_1', 'style': 'width:200px', 'onchange': 'populate_selected_product(this.id,this.value)' }), # 自定义选项显示文本为product_denomination label_from_instance=lambda obj: obj.product_denomination )
配合方案1的插件初始化代码,即可保证提交的是产品ID,验证通过。
额外注意事项
- 避免在表单定义时直接执行
list(models_products.objects.values_list(...)),此代码仅在表单加载时执行一次,新增产品不会同步到选项中。建议改用动态函数获取选项:def get_product_choices(): return list(models_products.objects.values_list('id_product', 'product_denomination')) # 字段中使用 choices=get_product_choices, - 使用ModelChoiceField时,queryset会在每次请求时获取最新数据,无需额外处理动态更新问题。
内容的提问来源于stack exchange,提问作者Nico44044
相关产品推荐
相关产品推荐

