一个视图中的多个 Django Crispy 表单

Multiple Django Crispy Forms In One View

当使用 form|crispy 过滤器在视图中放置 2 个表单时,此答案在单个视图中处理 2 个表单:Proper way to handle multiple forms on one page in Django I am getting this 错误。

views.py:

def test_form(request):
    if not request.user.is_authenticated():
        return redirect(settings.LOGIN_URL)
    title = 'test form'
    row_control_form = RowControlForm(request.POST or None)
    entry_form = EntryForm(request.POST or None)

    context = {
        'title': title,
        'row_control_form': row_control_form,
        'entry_form': entry_form,
    }

    if 'row_control_submit' in request.POST:
        if row_control_form.is_valid():
            row_control_form.save()

        if 'entry_submit' in request.POST:
            if entry_form.is_valid():
                entry_form.save()

    return render(request, "timesheet/test_form.html", context)

forms.py

class RowControlForm(forms.ModelForm):
    class Meta:
        model = RowControl
        fields = ['month_control_record', 'department', 'activity', 'notes']

    def clean(self):
        cleaned_data = self.cleaned_data
        # Ensures row is unique
        try:
            RowControl.objects.get(month_control_record=cleaned_data['month_control_record'],
                                   department=cleaned_data['department'],
                                   activity=cleaned_data['activity'],
                                   notes=cleaned_data['notes'])

        except RowControl.DoesNotExist:
            pass

        else:
            raise ValidationError('This row already exists')

        # Always return cleaned data
        return cleaned_data


class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['row_control', 'date', 'hours']

    def clean(self):
        cleaned_data = self.cleaned_data

        # Ensures data is unique (only 1 hours entry for each date and row_control)
        try:
            Entry.objects.get(row_control=cleaned_data['row_control'],
                              date=cleaned_data['date'])

        except Entry.DoesNotExist:
            pass

        else:
            raise ValidationError('This entry already exists')

        # Always return cleaned data
        return cleaned_data

test_form.html

{% extends "base.html" %}
{% load crispy_forms_tags %}


{% block content %}
<div class="col-md-6 col-md-offset-3">
  <h1 class="page-header"> Form Test </h1>
  <form method="POST" action="{{ request.path }}">
    {% csrf_token %}
    {{ row_control_form|crispy }}

    <button class="btn btn-primary" type="submit" value="Submit" name="row_control_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button>  </form>
  </br>
</div>

<div class="col-md-6 col-md-offset-3">
  <h1 class="page-header"> Form Test </h1>
  <form method="POST" action="{{ request.path }}">
    {% csrf_token %}
    {{ entry_form|crispy }}

    <button class="btn btn-primary" type="submit" value="Submit" name="entry_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button>  </form>
  </br>
</div>

{% endblock %}

为错误提供上下文: forms.py 的第 42 行是:

Entry.objects.get(row_control=cleaned_data['row_control'],

编辑:进一步的调查表明,无论按下哪个提交按钮,两个表单验证都是 运行,为 RowControlForm 提交有效数据时的 request.POST 是:

<QueryDict: {'csrfmiddlewaretoken': ['HffmmbI31Oe0tItYDfYC4MoULQHL0KvF'], 'notes': ['Cool'], 'row_control_submit': ['Submit'], 'month_control_record': ['1'], 'department': ['1'], 'activity': ['1']}>

因此 entry_submit 不在 request.POST 中并且验证不应该 运行 但它是?

首先,您需要修复表单 clean 方法的这一行

def clean(self):
    ...
    Entry.objects.get(row_control=cleaned_data['row_control'],

您不能假设 row_control 会出现在 cleaned_data 中。您需要添加检查 if 'row_control' in cleaned_data 或捕获 KeyError,然后适当更新方法的其余部分。您应该修复此问题,即使您在将多个表单放在一页上之前没有看到此错误。在 POST 请求中遗漏一个值应该不可能导致 500 服务器错误。即使页面上只有一个表单,用户也可以这样做。

两种形式的验证都是 运行,因为无论按下哪个提交按钮,您都使用 post 数据实例化两种形式。

row_control_form = RowControlForm(request.POST or None)
entry_form = EntryForm(request.POST or None)

您只应将 POST 数据用于您要提交的表单。

row_control_form = RowControlForm()
entry_form = EntryForm()

if 'row_control_submit' in request.POST:
    row_control_form = RowControlForm(request.POST)
    if row_control_form.is_valid():

if 'entry_submit' in request.POST:
    entry_form = EntryForm(request.POST)
    if entry_form.is_valid():
        entry_form.save()

最后,最好在用户成功提交有效表单后重定向用户。