django - 如何使 Django FormSet 接受 m2m 但将它们存储为多个 FK 记录?
问题描述
我需要为学生提供测试,但有些问题接受多个答案。
我可以让它创建多个记录来存储多个答案而不是使用 M2M 吗?
例如,如果学生为一个问题选择 A 和 B,则行为如下:
StudentAnwer.objects.create(question=question, answer=answer_a)
StudentAnwer.objects.create(question=question, answer=answer_b)
表单集.py
from django import forms
StudentAnswerFormSet = forms.inlineformset_factory(
Student,
StudentAnswer,
fields=['question', 'answer', ],
formset=forms.BaseInlineFormSet)
模型.py
class Question(BaseModel):
title = models.CharField(max_length=250)
is_multiple = models.BooleanField(default=False)
class StudentAnswer(BaseModel):
student = models.ForeignKey(Student, related_name='answers')
question = models.ForeignKey(Question, related_name='student_answers')
answer = models.ForeignKey(Answer, related_name='student_answers')
视图.py
from formtools.wizard.views import SessionWizardView
class StudentWizardView(SessionWizardView):
...
def get_answer_form(self, step=None, data=None, files=None):
...
question_list = get_question_list()
return forms.inlineformset_factory(
Student,
StudentAnswer,
fields=['question', 'answer', ],
extra=len(question_list),
formset= forms.BaseInlineFormSet)
def get_form(self, step=None, data=None, files=None):
form = super(StudentWizardView, self).get_form(step, data, files)
# determine the step if not given
if step is None:
step = self.steps.current
if step == self.STEP_ANSWER:
initial_dict = self.get_form_initial(step)
prev_form_data = self.get_cleaned_data_for_step(self.STEP_PROFILE)
form = self.get_answer_form(step, data, files)
for form_index in range(len(initial_dict.keys())):
question = initial_dict[form_index]['question']
answer_list = question.get_answer_list()
if question.is_multiple:
form.forms[form_index].fields['answer'] = ModelMultipleChoiceField(queryset=answer_list,
widget=CheckboxSelectMultiple)
else:
form.forms[form_index].fields['answer'].widget.choices.queryset = answer_list
我试图避免使用 M2M,以便更轻松地在模板上显示每个问题的可能答案。
{{ wizard.management_form }}
{{ wizard.form.management_form }}
{% for answer_form in wizard.form.forms %}
{{ answer_form.question }}
{{ answer_form.answer }}
{% for hidden in answer_form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endfor %}
解决方案
通过使用 rawform.data
而不是解决了它form.cleaned_data
,因此可以在清理之前访问同一字段的多个值。
这是有风险的,但您可以仅针对您希望它执行的部分复制清理方法。
还通过以下方式制作了一个自定义复选框列表:
{% for choice in field.field.choices %}
<input type="checkbox" id="id_{{ field.html_name }}_{{ forloop.counter }}" name="{{ field.html_name }}" value="{{ choice.id }}"{% if choice.id in field.value %} checked="checked"{% endif %}> {{ choice }}
{% endfor %}
并删除了自定义表单字段,所以它们都是:
form.forms[form_index].fields['answer'].widget.choices.queryset = answer_list
推荐阅读
- angular - 无法在 Angular 6 中读取 null 的属性“出口”
- php - CodeIgniter 中 update_batch 的多个 WHERE 条件问题
- javafx - JavaFX 和 FXML。组合框中的嵌套列表
- php - 电子邮件在 Plesk 服务器上不起作用
- ios - 迅速采用 Objective-C 协议打破了复杂的结构
- scala - Scala 案例类 Option 值链
- extjs - ExtJS groupclick 在标题单击时选择所有子元素
- javascript - 无法使用 ang-jsoneditor 更改带有 Angular 6 的 Json 编辑器的高度
- java - 额外的奇数符号被添加到序列化输出中
- c# - WPF如何减去绑定到DataGrid的数据库表的两列