外卖网站Django开发:渲染表单前为Commande模型plat字段赋值
解决方案:在表单中传递菜品实例并完成份数验证
你的核心问题在于表单的clean方法无法访问到对应的Plat实例——因为你没有把plat字段加入表单的fields列表,所以cleaned_data.get('plat')必然返回None,导致后续访问plat.nb_portions触发AttributeError。
你已经尝试在视图中把plat传递给表单,这方向是对的,接下来需要完善表单的实现:
1. 修改CommandeForm的初始化方法
在表单的__init__中接收plat参数,并将其保存为表单实例的属性,这样clean方法就能直接访问到:
# forms.py class CommandeForm(forms.ModelForm): class Meta: model = Commande fields = ['nb_portions', 'adresse_livraison'] widgets = { 'nb_portions': forms.NumberInput(attrs={'class': 'form-control'}), 'adresse_livraison': forms.TextInput(attrs={'class': 'form-control'}) } labels = { 'adresse_livraison': 'Adresse de livraison (livraison à partir de 5 portions) :' } def __init__(self, *args, **kwargs): # 从kwargs中取出plat参数,再调用父类初始化方法 self.plat = kwargs.pop('plat') super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() nb_portions = cleaned_data.get('nb_portions') # 先判断份数是否有效,避免None与整数比较的错误 if nb_portions is not None: if nb_portions > self.plat.nb_portions: # 注意add_error的用法:第一个参数是字段名,第二个是错误消息 self.add_error( 'nb_portions', f"Le nombre de portions maximales disponibles pour ce plat est : {self.plat.nb_portions} portions." ) return cleaned_data
2. 视图部分的验证
你已经修改了视图中表单的初始化方式,这部分是正确的:
# views.py def commander(request, id): plat = get_object_or_404(Plat, id=id) if request.method == "POST": form = CommandeForm(request.POST, plat=plat) if form.is_valid(): commande = form.save(commit=False) commande.user = request.user commande.plat = plat commande.date = plat.date_prep commande.save() return redirect(commande_validee) else: form = CommandeForm(plat=plat) return render(request, 'actualites/commander.html', locals())
关键修复点说明
- 传递
Plat实例到表单:通过__init__方法把plat传入并存储,避免依赖cleaned_data(因为表单不包含plat字段)。 - 完善
clean逻辑:先判断nb_portions是否存在,防止用户未填写时触发None > int的错误;同时修正add_error的用法,把错误绑定到nb_portions字段,让错误提示显示在对应输入框下方,提升用户体验。 - 避免直接修改表单实例的模型字段:之前的
commande.plat = plat是错误的,因为表单实例的plat不是模型字段,而是需要通过初始化参数传递。
这样修改后,表单就能正确验证所选份数是否超过菜品的可用份数,同时不会再触发AttributeError。
内容的提问来源于stack exchange,提问作者Julien Mertz
相关产品推荐
相关产品推荐

