Django案件卡片保存/编辑功能失效,请求故障排查
问题:修改BusinessCard模型后,案件卡片创建与编辑无法保存数据
终端输出日志
[03/Oct/2023 16:15:52] "GET /create/?csrfmiddlewaretoken=xgcCZx4x2RjaTeFT5Sn6OBqC3k9VZUBO69tG4zJMiNuVwne9NTX1LXpdRJqn03AZ&original_name=222&author=1&case_category=3&defendant=Петров&article=200&sides_case=3&preliminary_hearing=20.09.2023 HTTP/1.1" 200 8459
相关代码
Model
class BusinessCard(models.Model): '''7. Модель карточки по делу''' original_name = models.CharField( unique=True, max_length=100, verbose_name='номер дела' ) author = models.ForeignKey( User, models.SET_NULL, blank=True, null=True, related_name='cards', verbose_name='Автор карточки' ) case_category = models.ForeignKey( Category, blank=True, null=True, on_delete=models.SET_NULL, related_name='cards', verbose_name='Категория дела' ) defendant = models.CharField( max_length=150, verbose_name='подсудимый' ) under_arrest = models.BooleanField( verbose_name='под стражей' ) article = models.PositiveSmallIntegerField( verbose_name='Статья УК РФ' ) pub_date = models.DateTimeField( auto_now_add=True, verbose_name='Дата создания/изменения карточки' ) sides_case = models.ManyToManyField( SidesCase, verbose_name='Поля сторон по делу' ) preliminary_hearing = models.DateField( verbose_name='Дата предварительного слушания/(с/з)' ) class Meta: ordering = ('pub_date',) verbose_name = 'Карточка на дело' verbose_name_plural = 'карточка на дело' def __str__(self): return self.original_name
Views
@login_required def card_create(request): '''Шаблон создание карточки''' template = 'create_card.html' form = CardForm(request.POST or None) if form.is_valid(): card = form.save(commit=False) card.author = request.user card.save() form = CardForm() return redirect('business_card:profile', request.user) return render(request, template, {'form': form}) @login_required def card_edit(request, post_id): '''Шаблон редактирования карточки''' card = get_object_or_404( BusinessCard.objects.select_related( 'case_category', 'author'), id=post_id ) if request.user != card.author: return redirect('business_card:business_card_detail', card.pk) form = CardForm(request.POST or None, instance=card) if form.is_valid(): form.save() return redirect('business_card:business_card_detail', card.pk) form = CardForm(instance=card) context = { 'form': form, 'is_edit': True, 'card': card, } return render(request, 'create_card.html', context)
Forms
class CardForm(ModelForm): class Meta: model = BusinessCard fields = [ 'original_name', 'author', 'case_category', 'defendant', 'under_arrest', 'article', 'sides_case', 'preliminary_hearing' ] labels = { 'original_name': 'Номер дела', 'case_category': 'категория' } help_texts = { 'original_name': 'Номер нового дела', 'case_category': 'Категория' }
模板代码
{% extends 'base.html' %} {% block Title %}<title>{% if is_edit %} Редактировать карточку {% else%} Новая карточка {% endif %}</title>{% endblock %} {% block content %} <div class="container py-5"> <div class="row justify-content-center"> <div class="col-md-8 p-5"> <div class="card"> <div class="card-header"> {% if is_edit %} Редактировать карточку {% else%} Новая карточка {% endif %} </div> <div class="card-body"> {% if form.errors %} {% for field in form %} {% for error in field.errors %} <div class="alert alert-danger"> {{ error|escape }} </div> {% endfor %} {% endfor %} {% for error in form.non_field_errors %} <div class="alert alert-danger"> {{ error|escape }} </div> {% endfor %} {% endif %} <form method="card" {% if card %} "{% url 'business_card:card_edit' card.pk %}" {% else %} "{% url 'business_card:card_create' %}" {% endif %}> {% csrf_token %} {% for field in form %} <div class="form-group row my-3 p-3 textarea"> <label for="{{field.id_for_label}}"> {{ field.label }} {% if field.label.required %} <span class="required text-danger" >*</span> {% endif %} </label> {{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %} <div class="form-group row my-3 p-3"> <label for="{{ field.id_for_label }}"> {{ field.label }} </label> </div> <div class="d-flex justify-content-end"> <button type="submit" class="btn btn-primary"> {% if is_edit %} Сохранить {% else %} Добавить {% endif %} </button> </div> </form> </div> </div> </div> </div> </div> {% endblock %}
问题排查与修复
1. 表单提交方法错误(核心问题)
模板中表单的method属性被错误设置为"card",导致表单以GET方式提交,数据通过URL参数传递而非POST请求。视图中request.POST为空,form.is_valid()无法通过,数据无法保存。
修复模板中的form标签:
<form method="post" action="{% if card %}{% url 'business_card:card_edit' card.pk %}{% else %}{% url 'business_card:card_create' %}{% endif %}">
- 将
method="card"改为method="post" - 修正
action属性写法,移除多余引号,确保URL正确渲染
2. 多对多字段未保存
创建视图中使用commit=False保存模型实例后,多对多字段(sides_case)不会自动保存,必须调用form.save_m2m()完成关联数据存储。
修改card_create视图:
@login_required def card_create(request): '''Шаблон создание карточки''' template = 'create_card.html' form = CardForm(request.POST or None) if form.is_valid(): card = form.save(commit=False) card.author = request.user card.save() form.save_m2m() # 新增该行保存多对多关系 return redirect('business_card:profile', request.user) return render(request, template, {'form': form})
- 移除不必要的
form = CardForm(),避免清空表单状态 - 添加
form.save_m2m()保存多对多字段
3. 表单包含不必要的author字段
表单中包含author字段,但创建逻辑中手动设置当前登录用户为作者,前端无需显示该字段,否则可能导致提交冲突。
修改CardForm:
class CardForm(ModelForm): class Meta: model = BusinessCard fields = [ 'original_name', 'case_category', # 移除author字段 'defendant', 'under_arrest', 'article', 'sides_case', 'preliminary_hearing' ] labels = { 'original_name': 'Номер дела', 'case_category': 'категория' } help_texts = { 'original_name': 'Номер нового дела', 'case_category': 'Категория' }
4. 编辑视图错误重置表单
编辑视图中,当form.is_valid()不通过时重新实例化表单,导致表单错误信息丢失,用户无法看到验证失败原因。
修改card_edit视图:
@login_required def card_edit(request, post_id): '''Шаблон редактирования карточки''' card = get_object_or_404( BusinessCard.objects.select_related( 'case_category', 'author'), id=post_id ) if request.user != card.author: return redirect('business_card:business_card_detail', card.pk) form = CardForm(request.POST or None, instance=card) if form.is_valid(): form.save() return redirect('business_card:business_card_detail', card.pk) # 移除form = CardForm(instance=card),保留带错误信息的form context = { 'form': form, 'is_edit': True, 'card': card, } return render(request, 'create_card.html', context)
5. 模板中多余的字段标签
模板末尾有一段多余的字段标签代码,会导致变量错误(field在循环外未定义),需删除:
<div class="form-group row my-3 p-3"> <label for="{{ field.id_for_label }}"> {{ field.label }} </label> </div>
验证修复效果
完成上述修改后:
- 表单将以POST方式提交数据,视图能正确获取表单内容
- 多对多关系会被正常保存
- 表单不再显示不必要的author字段,避免冲突
- 验证失败时会显示错误信息,方便用户修正
- 多余代码被移除,避免模板错误
内容的提问来源于stack exchange,提问作者Андрей
相关产品推荐
相关产品推荐

