Django CreateView获取POST数据报错,请求修复MultiValueDictKeyError
问题描述
尝试通过PurchaseCreateView创建Purchase实例,同时更新Ingredients模型库存,但代码抛出MultiValueDictKeyError,无法自行解决,寻求协助。
模型代码
class Ingredient(models.Model): name = models.CharField(max_length=30) quantity = models.FloatField() price = models.FloatField() def __str__(self): return self.name def get_absolute_url(self): return "inventory" class MenuItem(models.Model): name = models.CharField(max_length=50) price = models.FloatField() def available(self): return all(x.enough() for x in self.reciperequirements_set.all()) def __str__(self): return self.name class RecipeRequirements(models.Model): menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE) ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE) quantity = models.FloatField(default=0) def enough(self): return self.quantity <= self.ingredient.quantity def __str__(self): return f"{self.menu_item} requires {self.ingredient}" class Purchase(models.Model): menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE) timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.menu_item} purchased at {self.timestamp}"
视图代码
class PurchaseCreateView(CreateView): template_name = "inventory/purchase_form.html" model = Purchase fields = "__all__" success_url = reverse_lazy("home") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["menu_items"] = [x for x in MenuItem.objects.all() if x.available()] return context def post(self, request): menu_item_id = request.POST["menuitem"] menu_item = MenuItem.objects.get(pk=menu_item_id) purchase = Purchase(menu_item=menu_item) purchase.save()
模板代码
{% extends 'base.html' %} {% block head %} <title>New purchase</title> {% endblock %} {% block content %} <h2>Add a new purchase</h2> <form method="POST"> {% csrf_token %} <select> {% for menu_item in menu_items %} <option name="menuitem" value="{{menu_item.id}}">{{menu_item.name}}</option> {% endfor %} </select> <input type="submit" value="Save"> </form> {% endblock %}
问题分析与解决方案
错误原因
- 表单提交无有效键值:模板中
<select>标签未设置name="menuitem",仅给<option>加name无效,导致POST请求中没有menuitem字段,触发MultiValueDictKeyError。 - 视图方法未归属于类:
get_context_data和post方法未缩进,不属于PurchaseCreateView类,无法被视图调用。 - 未实现库存更新逻辑:当前代码仅创建Purchase实例,未根据RecipeRequirements扣除对应Ingredient的数量。
修正后的代码
模板代码(修正select标签)
{% extends 'base.html' %} {% block head %} <title>New purchase</title> {% endblock %} {% block content %} <h2>Add a new purchase</h2> <form method="POST"> {% csrf_token %} <!-- 给select添加name属性,这才是表单提交的键 --> <select name="menuitem"> {% for menu_item in menu_items %} <option value="{{menu_item.id}}">{{menu_item.name}}</option> {% endfor %} </select> <input type="submit" value="Save"> </form> {% endblock %}
视图代码(修正缩进+添加库存更新)
from django.views.generic.edit import CreateView from django.urls import reverse_lazy from .models import Purchase, MenuItem, RecipeRequirements, Ingredient class PurchaseCreateView(CreateView): template_name = "inventory/purchase_form.html" model = Purchase fields = "__all__" success_url = reverse_lazy("home") # 缩进归属于类 def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["menu_items"] = [x for x in MenuItem.objects.all() if x.available()] return context # 缩进归属于类,添加异常处理和库存更新 def post(self, request): # 使用get避免KeyError,同时处理未选择的情况 menu_item_id = request.POST.get("menuitem") if not menu_item_id: # 可根据需求返回错误提示,这里直接跳转回表单 return self.render_to_response(self.get_context_data()) try: menu_item = MenuItem.objects.get(pk=menu_item_id) # 创建Purchase记录 purchase = Purchase(menu_item=menu_item) purchase.save() # 更新对应原料库存 for req in menu_item.reciperequirements_set.all(): ingredient = req.ingredient ingredient.quantity -= req.quantity ingredient.save() return super().form_valid(purchase) except MenuItem.DoesNotExist: # 处理不存在的MenuItem,返回表单页面 return self.render_to_response(self.get_context_data())
内容的提问来源于stack exchange,提问作者Tommy Gee
相关产品推荐
相关产品推荐

