如何在Django中用单个表单保存多个模型对象实例?
解决方案
1. 修正模板循环与表单结构
首先你的模板遍历AnswerItem的写法有误,因为AnswerMenu和AnswerItem是外键关联,需要通过反向关联获取对应选项。同时要把所有单选框包裹在一个<form>标签内:
<form method="post" action="{% url '你的提交视图URL名称' %}"> {% csrf_token %} {% for answer_menu in answer_menus %} <div>{{ answer_menu.menu_name }}</div> {% for answer_item in answer_menu.answeritem_set.all %} <p> <label> <input id="{{ answer_item.pk }}" name="{{ answer_menu.pk }}" type="radio" value="{{ answer_item.pk }}"> {{ answer_item.item_name }} </label> </p> {% endfor %} {% endfor %} <button type="submit">提交答案</button> </form>
注意:
- 给单选框添加
value="{{ answer_item.pk }}",提交时才能传递选中选项的ID - 用
answer_menu.answeritem_set.all()遍历该菜单下的所有选项(如果给AnswerMenu的answer_item字段设置了related_name,比如related_name='items',就改成answer_menu.items.all())
2. 视图中处理POST请求并批量创建实例
可以用基于函数的视图,或是给现有ListView添加POST处理逻辑:
基于函数的视图示例
from django.shortcuts import redirect, get_object_or_404 from .models import AnswerMenu, AnswerItem, QuestionAnswer def submit_answers(request): if request.method == 'POST': # 遍历POST键值对,键是AnswerMenu的PK,值是选中的AnswerItem的PK for menu_pk, item_pk in request.POST.items(): # 排除csrf令牌等非菜单PK的键 if menu_pk == 'csrfmiddlewaretoken': continue try: menu = get_object_or_404(AnswerMenu, pk=menu_pk) item = get_object_or_404(AnswerItem, pk=item_pk) # 创建QuestionAnswer实例 QuestionAnswer.objects.create(answer=menu, answer_item=item) except ValueError: # 处理非法PK值 continue # 提交成功后重定向到结果页或原页面 return redirect('结果页URL名称') # GET请求直接重定向到菜单展示页 return redirect('菜单展示页URL名称')
基于类的ListView扩展POST处理
如果用的是ListView,重写post方法:
from django.views.generic import ListView from django.urls import reverse from .models import AnswerMenu, AnswerItem, QuestionAnswer class AnswerMenuListView(ListView): model = AnswerMenu context_object_name = 'answer_menus' template_name = '你的模板路径.html' def post(self, request, *args, **kwargs): for menu_pk, item_pk in request.POST.items(): if menu_pk == 'csrfmiddlewaretoken': continue try: menu = AnswerMenu.objects.get(pk=menu_pk) item = AnswerItem.objects.get(pk=item_pk) QuestionAnswer.objects.create(answer=menu, answer_item=item) except (AnswerMenu.DoesNotExist, AnswerItem.DoesNotExist, ValueError): continue return redirect(self.get_success_url()) def get_success_url(self): # 定义提交成功后的跳转URL return reverse('结果页URL名称')
3. 性能优化建议
如果菜单数量较多,用bulk_create批量创建实例提升效率:
question_answers = [] for menu_pk, item_pk in request.POST.items(): if menu_pk == 'csrfmiddlewaretoken': continue try: menu = AnswerMenu.objects.get(pk=menu_pk) item = AnswerItem.objects.get(pk=item_pk) question_answers.append(QuestionAnswer(answer=menu, answer_item=item)) except (AnswerMenu.DoesNotExist, AnswerItem.DoesNotExist, ValueError): continue QuestionAnswer.objects.bulk_create(question_answers)
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

