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

Django Update方法仅更新单个字段时出错的原因排查

问题

我有一个Usecase模型,包含kpi和usecase_type两个外键字段。我编写了一个通过下拉列表更新这两个字段的方法,但该方法仅在同时修改两个下拉选项时生效,仅更新单个下拉列表选项时会报错,请问可能的原因是什么?

相关代码

views.py 代码

def edit_usecase(request, ucid):
    try:
        usecase_details = Usecase.objects.filter(usecase_id=ucid)
        context = {"usecase_details":usecase_details[0], "usecase_types": UsecaseType.objects.all(), "usecase_kpis": Kpi.objects.all()}        

        if request.method == "POST":
            usecase_type = request.POST['usecase_type']
            kpi = request.POST['kpi']
            usecase_details = Usecase.objects.get(usecase_id=ucid)
            usecase_details.usecase_type_id=usecase_type
            usecase_details.kpi_id=kpi
            usecase_details.save()
            
            if usecase_details:
                messages.success(request, "Usecase Data was updated successfully!")
                return HttpResponseRedirect(reverse('usecase-details', args=[ucid]))
            else:
                messages.error(request, "Some Error was occurred!")
                return HttpResponseRedirect(reverse('update-usecase', args=[ucid]))
        return render(request, 'UpdateUsecase.html', context)
    except:
        messages.error(request, "Some Error was occurred!")
        return HttpResponseRedirect(reverse('update-usecase', args=[ucid]))

模板代码

<div class="row  d-flex">
    <div class="col-12 mb-4">
        <div class="card border-light shadow-sm components-section  d-flex ">
            <div class="card-body  d-flex ">
                <div class="row mb-4">                 
                    <div class="card-body">
                        <div class="row col-12">
                        <div class="mb-4">
                            <h4 class="h4 uc-header">Edit usecase details:</h4>
                        </div>
                        <!-- <li role="separator" class="dropdown-divider border-black mb-3 ml-3"></li> -->
                        <form action="/update-usecase/{{usecase_details.usecase_id}}" method="POST">
                            {% csrf_token %}
                            <div class="form-row mb-4">
                              <div class="col-lg-8 mr-f">
                                <label class="h6" for="project-name">Usecase Type:</label>
                                <select name="usecase_type" class="custom-select my-1 mr-sm-2" id="usecase_type">
                                    <option value="0" selected>{{usecase_details.usecase_type.usecase_type_name}}</option>
                                    {% for usecase_type in usecase_types %}
                                    <option value="{{ usecase_type.usecase_type_id }}">{{ usecase_type.usecase_type_name }}</option>
                                    {% endfor %}                             
                                  </select>                          
                                </div>
                                <div class="col-lg-8 mr-f">
                                  <label class="h6" for="project-name">Usecase KPI:</label>
                                  <select name="kpi" class="custom-select my-1 mr-sm-2" id="kpi">
                                      <option value="0" selected>{{usecase_details.kpi.kpi_name}}</option>
                                      {% for kpi in usecase_kpis %}
                                      <option value="{{ kpi.kpi_id }}">{{ kpi.kpi_name }}</option>
                                      {% endfor %}                             
                                    </select>                          
                                  </div> 
                            </div>
                            <input type="submit" class="btn btn-primary" value="Submit Changes">
                          </form>
                    </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

问题原因及解决办法

核心原因

  1. 默认选项值错误:模板中两个下拉框的默认选项(显示当前值的选项)value被设置为0,而数据库中外键字段对应的UsecaseType和Kpi实例ID都是大于0的有效值。当用户只修改其中一个下拉框时,另一个下拉框会提交0给后端,0不是有效的外键ID,触发数据库外键约束错误。
  2. 后端无参数验证:后端直接通过request.POST['xxx']获取值并赋值给外键字段,没有判断值是否有效,也没有保留原字段的值,导致无效值覆盖原有数据。

解决步骤

1. 修复模板中的默认选项值

将两个下拉框的默认选项value改为当前实例的对应外键ID,同时排除已选中的选项避免重复:

<!-- 修复后的Usecase Type下拉框 -->
<select name="usecase_type" class="custom-select my-1 mr-sm-2" id="usecase_type">
    <option value="{{usecase_details.usecase_type_id}}" selected>{{usecase_details.usecase_type.usecase_type_name}}</option>
    {% for usecase_type in usecase_types %}
        {% if usecase_type.usecase_type_id != usecase_details.usecase_type_id %}
        <option value="{{ usecase_type.usecase_type_id }}">{{ usecase_type.usecase_type_name }}</option>
        {% endif %}
    {% endfor %}                             
</select>

<!-- 修复后的KPI下拉框 -->
<select name="kpi" class="custom-select my-1 mr-sm-2" id="kpi">
    <option value="{{usecase_details.kpi_id}}" selected>{{usecase_details.kpi.kpi_name}}</option>
    {% for kpi in usecase_kpis %}
        {% if kpi.kpi_id != usecase_details.kpi_id %}
        <option value="{{ kpi.kpi_id }}">{{ kpi.kpi_name }}</option>
        {% endif %}
    {% endfor %}                             
</select>

2. 后端增加参数验证逻辑

获取POST参数时,先验证是否为有效ID,无效则保留原字段值,同时捕获具体异常方便排查:

def edit_usecase(request, ucid):
    try:
        usecase_details = Usecase.objects.get(usecase_id=ucid)
        context = {"usecase_details": usecase_details, "usecase_types": UsecaseType.objects.all(), "usecase_kpis": Kpi.objects.all()}        

        if request.method == "POST":
            # 获取参数,默认空值
            usecase_type = request.POST.get('usecase_type', '')
            kpi = request.POST.get('kpi', '')
            
            # 验证Usecase Type是否有效
            if usecase_type.isdigit() and UsecaseType.objects.filter(usecase_type_id=usecase_type).exists():
                usecase_details.usecase_type_id = usecase_type
            
            # 验证KPI是否有效
            if kpi.isdigit() and Kpi.objects.filter(kpi_id=kpi).exists():
                usecase_details.kpi_id = kpi
            
            usecase_details.save()
            messages.success(request, "Usecase Data was updated successfully!")
            return HttpResponseRedirect(reverse('usecase-details', args=[ucid]))
            
        return render(request, 'UpdateUsecase.html', context)
    except Usecase.DoesNotExist:
        messages.error(request, "Usecase not found!")
        return HttpResponseRedirect(reverse('update-usecase', args=[ucid]))
    except Exception as e:
        messages.error(request, f"Error occurred: {str(e)}")
        return HttpResponseRedirect(reverse('update-usecase', args=[ucid]))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 21:14:59