使用CreateView与ManyToMany字段创建食材时字段类型错误求助
问题描述
使用jQuery插件Selectize.js实现了一个允许用户创建食材的表单,但提交表单时出现错误:Field 'id' expected a number but got 'b'——系统把食材名称的每个字符当成了需要匹配数据库的条目ID,而非整个单词。相关代码如下:
表单代码(forms.py)
class EatForm(forms.ModelForm): notes = forms.CharField(widget=forms.Textarea(attrs={"rows":3}),required=False) ingredient = forms.CharField(widget=forms.TextInput(),required=False) allergen = forms.CharField(widget=forms.TextInput(),required=False) class Meta: model = Eat fields = ["name", "notes", "meal","ingredient","allergen","favorite","datetime"]
模型代码
Eat模型
class Eat(models.Model): MEAL_CHOICES = ( ("breakfast", "Breakfast"), ("lunch", "Lunch"), ("dinner", "Dinner"), ("supper", "Supper"), ("snack", "Snack"), ("drink", "Drink"), ) id = models.BigAutoField(primary_key=True, editable=False) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=255) notes = models.CharField(max_length=255, blank=True, null=True) meal = models.CharField(choices=MEAL_CHOICES, max_length=20) ingredient = models.ManyToManyField(Ingredient,blank=True) allergen = models.ManyToManyField(Allergen,blank=True) favorite = models.BooleanField(default=False) datetime = models.DateTimeField()
Ingredient模型
class Ingredient(models.Model): id = models.BigAutoField(primary_key=True, editable=False) name = models.CharField(max_length=255, unique=True) def __str__(self): return self.name
视图代码(views.py)
class EatFormAddView(LoginRequiredMixin, CreateView): model = Eat template_name = "dashboard.html" success_url = reverse_lazy("core:activity") form_class = EatForm def form_valid(self, form): form.instance.user = self.request.user ingredients = form.cleaned_data['ingredient'].split(',') for ingredient in ingredients: try: ingredient_obj = Ingredient.objects.get(name=ingredient) except: ingredient_obj = Ingredient(name=ingredient) ingredient_obj.save() return super().form_valid(form)
错误原因
- 表单里的
ingredient用了CharField,但模型中是ManyToManyField——Django默认会期望这个字段接收关联模型的ID列表,而非字符串。 - 虽然你在
form_valid里做了字符串分割处理,但调用父类的form_valid时,原始的字符串值会被Django尝试解析为ManyToMany的关联ID,导致逐个字符被当成ID处理,触发类型错误。 - Selectize.js的配置可能没跟上,提交的格式不符合Django对ManyToMany字段的预期。
解决方案
1. 修改表单字段类型与验证逻辑
把表单中的ingredient和allergen替换为ModelMultipleChoiceField,并自定义验证逻辑支持创建新食材:
# forms.py from django import forms from .models import Eat, Ingredient, Allergen class EatForm(forms.ModelForm): notes = forms.CharField(widget=forms.Textarea(attrs={"rows":3}), required=False) # 替换CharField为ModelMultipleChoiceField,指定用name字段匹配 ingredient = forms.ModelMultipleChoiceField( queryset=Ingredient.objects.all(), required=False, widget=forms.TextInput(), to_field_name='name' ) allergen = forms.ModelMultipleChoiceField( queryset=Allergen.objects.all(), required=False, widget=forms.TextInput(), to_field_name='name' ) class Meta: model = Eat fields = ["name", "notes", "meal","ingredient","allergen","favorite","datetime"] # 重写clean方法处理新食材创建 def clean_ingredient(self): # 获取提交的所有食材名称(可能是多个) raw_ingredients = self.data.getlist('ingredient') ingredient_objects = [] for name in raw_ingredients: name = name.strip() if not name: continue # 自动创建或获取已有食材 obj, created = Ingredient.objects.get_or_create(name=name) ingredient_objects.append(obj) return ingredient_objects # 同理处理allergen字段 def clean_allergen(self): raw_allergens = self.data.getlist('allergen') allergen_objects = [] for name in raw_allergens: name = name.strip() if not name: continue obj, created = Allergen.objects.get_or_create(name=name) allergen_objects.append(obj) return allergen_objects
2. 简化视图逻辑
移除视图中手动处理食材的代码,因为表单的clean方法已经完成了创建和获取:
# views.py class EatFormAddView(LoginRequiredMixin, CreateView): model = Eat template_name = "dashboard.html" success_url = reverse_lazy("core:activity") form_class = EatForm def form_valid(self, form): form.instance.user = self.request.user # 无需再手动处理ingredient/allergen,表单已处理完成 return super().form_valid(form)
3. 配置Selectize.js提交格式
确保Selectize.js提交的是食材名称列表,而非单个字符串:
$(document).ready(function() { // 配置ingredient字段的Selectize $('#id_ingredient').selectize({ plugins: ['remove_button'], delimiter: ',', // 设置逗号为分隔符,匹配表单处理逻辑 persist: false, create: function(input) { return { value: input, text: input }; }, valueField: 'name', // 提交食材名称而非ID labelField: 'name', searchField: 'name' }); // 配置allergen字段的Selectize $('#id_allergen').selectize({ plugins: ['remove_button'], delimiter: ',', persist: false, create: function(input) { return { value: input, text: input }; }, valueField: 'name', labelField: 'name', searchField: 'name' }); });
内容的提问来源于stack exchange,提问作者bigredthelogger
相关产品推荐
相关产品推荐

