You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 %}

问题分析与解决方案

错误原因

  1. 表单提交无有效键值:模板中<select>标签未设置name="menuitem",仅给<option>加name无效,导致POST请求中没有menuitem字段,触发MultiValueDictKeyError。
  2. 视图方法未归属于类:get_context_data和post方法未缩进,不属于PurchaseCreateView类,无法被视图调用。
  3. 未实现库存更新逻辑:当前代码仅创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:25:10