Django模型表单:显示查询表单并处理OneToMany字段的发布请求

时间:2015-12-19 10:22:52

标签: python django django-models

我是Django的新手,在google / SO上搜索后找不到类似的问题。

我有一个名为Questions的模型,它有多个(2-4)选项,定义如下:

class Question(models.Model):
  name = models.CharField(max_length=128)
class Choice(models.Model):
  name = models.CharField(max_length=256)     
  is_correct = models.BooleanField(default=False) 
  question = models.ForeignKey(Question, on_delete=models.CASCADE)

在多个选择中,只有一个是正确的。

我想做的事:在一个页面中,用户可以提交一个问题和多个选项,这是一个UI草稿:

enter image description here

我的第一个问题:我已经定义了ModelForm但不知道如何添加"选择"字段到QuestionForm:

class QuestionForm(ModelForm):
  name = forms.CharField(max_length=128)
  description = forms.CharField(max_length=256)
  class Meta:
    model = Question
    fields = ['name', 'description']

class ChoiceForm(ModelForm):
  name = forms.CharField(max_length=256)
  is_correct = forms.BooleanField()
  class Meta:
    model = Choice
    fields = ['name', 'is_correct']

除了手动编写之外,是否可以使用ModelForm渲染上面的HTML页面?

我的第二个问题:如果使用点击次数"提交"按钮,我使用AJAX将json数据发送到后端服务器,这里是表单数据的一个例子:

name:question1
choices[][name]:choice1
choices[][is_correct]:1
choices[][name]:choice2
choices[][is_correct]:0

这是我处理请求的代码:

form = QuestionForm(request.POST)
if form.is_valid():
  question = form.save()

如何解析请求中的选择? 我如何从POST请求中解析多个选择部分的数据?

同样,我是Django的新手,我们非常感谢任何答案/建议。

2 个答案:

答案 0 :(得分:1)

要为具有OneToMany关系的模型创建表单,我建议您使用Django的内联表单集:https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#inline-formsets

这是为相关模型创建表单的一种非常简单而优雅的方式。

要解析选项,用户输入的内容可以覆盖表单的clean方法。在此,通常检查并准备用户内容以将其存储到数据库中。 https://docs.djangoproject.com/en/1.8/ref/forms/validation/#form-field-default-cleaning

所以清洁可能如下所示:

class QuestionForm(ModelForm):
    ...
    def clean(self):
        cleaned_data = super(QuestionForm, self).clean()
        if cleaned_data['choice_name'].startswith('Something'):
            raise forms.ValidationError(
                "Choice names cannot start with 'Something'!"
            )

答案 1 :(得分:1)

您的模型似乎是正确的,为了能够在模板中添加多个选项,您需要formset。此外,您可以将formset和表单放在模板中的相同html表单中,并单独验证它们。每个人只关心与他们相关的POST数据。类似的东西:

<强> template.html

<form method="post" action="">
    {% csrf_token %}
    {{ choices_formset.management_form }} <!-- used by django to manage formsets -->
    {{ question_form.as_p }}
    {% for form in choices_formset %}
        {{ form.as_p }}
    {% endfor %}
  <button type='submit'>Submit</button>
</form>

<强> views.py

from django.db import IntegrityError, transaction
from django.shortcuts import redirect
from django.forms.formsets import formset_factory
from django.core.urlresolvers import reverse

def new_question(request):
    ChoicesFormset = formset_factory(ChoicesForm)
    if request.method == 'POST':
        question_form = QuestionForm(request.POST)
        choices_formset = ChoicesFormset(request.POST)
        if question_form.is_valid():
            question = Question(**question_form.cleaned_data)
            if choices_formset.is_valid():
                question.save()
                new_choice_list = list()
                append_choice = new_choice_list.append
                for form in choices_formset:
                    form.cleaned_data.update({'question': question})
                    append_choice(Choice(**form.cleaned_data))
                try:
                    with transaction.atomic():
                        Choice.objects.bulk_create(new_choice_list)
                except IntegrityError as e:
                    raise IntegrityError
        return redirect(reverse('question-detail-view', kwargs={'id': question.id}))

def question_detail(request, id):
    question_list = Question.objects.get(id=id)
    return render(request, 'question_detail.html', {'question_list': question_list})

<强> urls.py

url(r'^question/$', new_question, name='new-question-view'),
url(r'^question/(?P<id>\d+)/$', question_detail, name='question-detail-view'),

如果你想使用Ajax提交而不是django形式sumbission 检查 tutoriel