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

Django OneToOneField关联模型插入记录失败问题排查

Django ItemSettings 插入失败问题排查与修复

问题背景

已定义ItemMaster和ItemSettings一对一关联模型,ItemSettings初始无数据。为每个ItemMaster的物料编号设置“规则”链接,用于添加/编辑对应ItemSettings,要求ItemNumber不以下拉框形式展示,但当前代码无法向ItemSettings插入记录,需排查错误。

现有代码

Models.py

class ItemMaster(models.Model):
    project_name = models.ForeignKey(ProjectMaster, null=True, on_delete=models.SET_NULL)
    item_number = models.CharField(max_length=15, unique=True, error_messages={
        'unique': "This Item Number Already Exists!"})
    item_type = models.ForeignKey(ItemType, null=True, on_delete=models.SET_NULL)
    item_description = models.CharField(max_length=255, blank=True, null=True)
    
    def __str__(self):
        return self.item_number

class ItemSettings(models.Model):
    item_number = models.OneToOneField(ItemMaster, on_delete=models.CASCADE)
    low_at = models.FloatField(default=0)
    minimum_value = models.FloatField(default=0)
    maximum_value = models.FloatField(default=0)
    
    def __str__(self):
        return str(self.item_number)

Views.py

def itemsettings_edit(request, pkey):
    item_master_data = ItemMaster.objects.get(id=pkey)
    item_no = item_master_data.item_number
    form = ItemSettingsForm()
    if request.method == "GET":
        return render(request, 'masters/edit.html', {'item_no': item_no})
    elif request.method == 'POST':
        try:
            item_number = request.POST['item_no']
            low_at = request.POST['low_at']
            minimum_value = request.POST['minimum_value']
            maximum_value = request.POST['maximum_value']
            form = ItemSettingsForm(request.POST)
            ItemSettings(item_number=item_number, low_at=low_at, 
minimum_value=minimum_value, maximum_value=maximum_value).save()
            messages.SUCCESS(request, 'Data Saved')
        except Exception as e:
            messages.SUCCESS(request, 'Data Already Added!!!')
    return render(request, 'masters/edit.html', {'item_no': item_no, 'form': form})

edit.html

<form action="" enctype="multipart/form-data" method="POST" novalidate>
    {% csrf_token %}
    <table>
        <tr>
            <td>Item No.</td>
            <td><input name="item_number" readonly type="text" value="{{ item_no }}"></td>
        </tr>
        <tr>
            <td>Low At</td>
            <td><input name="low_at" type="text"></td>
        </tr>
        <tr>
            <td>Minimum</td>
            <td><input name="minimum_value" type="text"></td>
        </tr>
        <tr>
            <td>Maximum</td>
            <td><input name="maximum_value" type="text"></td>
        </tr>
    </table>
    <div class="card-action">
        <button class="btn waves-effect waves-light btn-small" name="action"
                type="submit">Save
        </button>
        <a class="btn grey darken-3 waves-effect waves-light btn-small"
           href="{% url 'itemMaster_list' %}">Cancel</a>
    </div>
</form>

错误点分析

  1. POST参数获取错误:模板中Item No的inputname为item_number,但视图里用request.POST['item_no']获取,会触发KeyError直接进入异常分支。
  2. 关联字段赋值错误:ItemSettings的item_number是OneToOne关联ItemMaster,必须传入ItemMaster对象或其主键ID,不能直接传字符串类型的物料编号。
  3. Messages调用错误:Django的messages方法为小写开头,messages.SUCCESS应为messages.success,否则会报错。
  4. 未处理编辑场景:若ItemSettings已存在,直接创建会触发唯一性约束异常,且GET请求未加载已有数据到表单。
  5. 缺乏数据验证:直接将POST字符串存入FloatField,若输入非数字会报错,且未使用表单验证逻辑。

修复后的代码

Views.py 修正版

from django.contrib import messages

def itemsettings_edit(request, pkey):
    item_master = ItemMaster.objects.get(id=pkey)
    # 自动处理新增/编辑:存在则获取,不存在则新建
    item_settings, created = ItemSettings.objects.get_or_create(item_number=item_master)
    
    if request.method == "GET":
        # 绑定已有数据到表单,实现编辑回显
        form = ItemSettingsForm(instance=item_settings)
        return render(request, 'masters/edit.html', {'item_no': item_master.item_number, 'form': form})
    elif request.method == 'POST':
        form = ItemSettingsForm(request.POST, instance=item_settings)
        if form.is_valid():
            form.save()
            messages.success(request, '数据保存成功')
        else:
            messages.error(request, '输入数据有误,请检查')
        return render(request, 'masters/edit.html', {'item_no': item_master.item_number, 'form': form})

补充表单类(forms.py)

隐藏item_number字段,避免前端出现下拉框:

from django import forms
from .models import ItemSettings

class ItemSettingsForm(forms.ModelForm):
    class Meta:
        model = ItemSettings
        fields = ['low_at', 'minimum_value', 'maximum_value']

edit.html 简化版

<form action="" method="POST" novalidate>
    {% csrf_token %}
    <table>
        <tr>
            <td>Item No.</td>
            <td><input name="item_number" readonly type="text" value="{{ item_no }}"></td>
        </tr>
        {{ form.as_table }}
    </table>
    <div class="card-action">
        <button class="btn waves-effect waves-light btn-small" type="submit">Save</button>
        <a class="btn grey darken-3 waves-effect waves-light btn-small" href="{% url 'itemMaster_list' %}">Cancel</a>
    </div>
</form>

关键修复说明

  • 用get_or_create确保每个ItemMaster对应唯一的ItemSettings,同时兼容新增和编辑场景。
  • 表单绑定instance实现数据回显和更新,通过form.is_valid()验证数据,避免类型错误。
  • 修正messages调用方式,确保提示正常显示。
  • 表单类隐藏item_number字段,后端直接用视图中获取的ItemMaster对象关联,无需从POST获取,符合需求。

内容的提问来源于stack exchange,提问作者chlorinelemon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 12:50:16