Django中如何更新M2M字段关联对象的属性值
Django多对多关联候选人票数更新实现
直接按以下步骤修改即可,代码可直接复用。
模板层修正
原模板存在循环变量重名、输入框无标识、缺少CSRF令牌三个问题,替换为如下代码:
<h5>{{ object }}</h5> <form method="POST"> {% csrf_token %} {{ form.as_p }} <hr> <h6>关联候选人票数设置</h6> {% for candidate in object.candidates.all %} <div style="margin: 10px 0;"> <label>{{ candidate.name }}:</label> <input type="number" min="0" name="candidate_votes_{{ candidate.id }}" value="{{ candidate.votes }}" required> </div> {% endfor %} <button type="submit">保存全部修改</button> </form>
输入框统一用
candidate_votes_候选人ID的命名规则,后端可以直接解析匹配,前端加min=0限制非法负数输入。
视图层逻辑补全
POST处理逻辑加了数据合法性校验和事务保证,避免脏数据:
from django.db import transaction from django.http import HttpResponseBadRequest from django.shortcuts import redirect, render def constituency_detail(request, pk): obj = get_object_or_404(Constituencies, id=pk) form = ConstituenciesForm(request.POST or None, instance=obj) if request.method == 'POST' and form.is_valid(): # 开启数据库事务,所有更新要么全成功要么全回滚 with transaction.atomic(): constituency = form.save() # 预取当前选区合法的候选人ID集合,防止越权修改 valid_candidate_ids = set(constituency.candidates.values_list('id', flat=True)) for param_key, param_val in request.POST.items(): if param_key.startswith('candidate_votes_'): candidate_id = param_key.split('_')[-1] # 校验参数合法性 if not candidate_id.isdigit() or int(candidate_id) not in valid_candidate_ids: return HttpResponseBadRequest("请求参数非法") if not param_val.isdigit(): return HttpResponseBadRequest("票数必须是非负整数") # 更新对应候选人票数 Candidates.objects.filter(id=int(candidate_id)).update(votes=int(param_val)) # 保存成功后重定向,避免刷新重复提交 return redirect('constituency_detail', pk=pk) return render(request, 'template.html', {'object': obj, 'form': form})
注意点
- 不要直接信任前端传的候选人ID,必须校验该候选人确实属于当前选区,防止恶意构造请求篡改非关联候选人数据
- 事务是必须的,避免出现选区信息更新成功、但部分候选人票数更新失败的数据不一致问题
- 所有输入值必须做类型校验,避免非数字值写入数据库触发异常
内容的提问来源于stack exchange,提问作者afi
相关产品推荐
相关产品推荐

