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>
问题原因及解决办法
核心原因
- 默认选项值错误:模板中两个下拉框的默认选项(显示当前值的选项)
value被设置为0,而数据库中外键字段对应的UsecaseType和Kpi实例ID都是大于0的有效值。当用户只修改其中一个下拉框时,另一个下拉框会提交0给后端,0不是有效的外键ID,触发数据库外键约束错误。 - 后端无参数验证:后端直接通过
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
相关产品推荐
相关产品推荐

