Django InlineFormSet验证不会引发ValidationError

时间:2018-07-12 09:35:27

标签: django

我在验证InlineFormSet的数据时遇到麻烦。我想要的是至少需要在表单集中输入一个资格。但是每次我用空的Qualification按下提交按钮时,都不会引发ValidationError。

这是我的代码:

forms.py

class QualificationForm(forms.ModelForm):

    class Meta:
        model = Qualification
        fields = ['qualification']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.disable_csrf = True
        self.helper.help_text_inline = True
        self.helper.label_class = 'col-sm-4'
        self.helper.field_class = 'col-sm-8'
        self.helper.layout = Layout(
            Field('qualification')
        )


class QualificationCustomInlineFormSet(forms.BaseInlineFormSet):

    def clean(self):
        cleaned_data = super().clean()
        for form in self.forms:
            qualification = cleaned_data.get('qualification', '').strip()
            if not qualification:
                msg = "Please enter qualification."
                self.add_error('qualification', msg)
                raise forms.ValidationError(msg, "error")

        return cleaned_data

views.py

class JobAddView(LoginRequiredMixin, SuccessMessageMixin, FormView):

    template_name = 'cepalco_website_admin/job_form.html'
    form_class = forms.JobForm
    success_url = reverse_lazy('cepalco_website_admin:home')
    success_message = "Successfully added %(job_title)s!"
    head_title = "Add new job"
    title_text = head_title
    description = "Enter the following job information"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['head_title'] = self.head_title
        context['title_text'] = self.title_text
        context['description'] = self.description
        QualificationInlineFormSet = inlineformset_factory(
            Job, Qualification,
            form=forms.QualificationForm, formset=forms.QualificationCustomInlineFormSet,
            extra=0, can_delete=False, min_num=1
        )
        WorkAssignmentInlineFormSet = inlineformset_factory(
            Job, WorkAssignment, form=forms.WorkAssignmentForm,
            extra=0, can_delete=False, min_num=1
        )
        context['qualification_inlineformset'] = QualificationInlineFormSet
        context['work_assignment_inlineformset'] = WorkAssignmentInlineFormSet
        return context

模板

{% extends 'cepalco_website_admin/base_admin_main.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block head_title %}{{ head_title }} | {{ block.super }}{% endblock head_title %}
{% block head_css %}
  {{ block.super }}
  {% include 'cepalco_website_admin/no_asteriskfield.html' %}
{% endblock head_css %}
{% block content_main %}
          <div class="title-bar">
            <h1 class="title-bar-title">
              <span class="d-ib">{{ title_text }}</span>
            </h1>
            <p class="title-bar-description">
              <small>{{ description }}</small>
            </p>
          </div>
          <div class="row">
            <div class="col-md-8">
              <div class="demo-form-wrapper">
                <form action="{% url 'cepalco_website_admin:job_add' %}" class="form form-horizontal" id="id_jobform" method="post">
                  {% csrf_token %}
                  <div class="divider">
                    <div class="divider-content"><h4>Job Information</h4></div>
                  </div>
                  <!-- <legend>Job Information</legend> -->
                  {% crispy form %}
                  <div class="divider">
                    <div class="divider-content"><h4>Qualifications</h4></div>
                  </div>
                  <!-- <legend>Qualifications</legend> -->
                  <div id="id_{{ qualification_inlineformset.prefix }}">
                  {% crispy qualification_inlineformset qualification_inlineformset.form.helper %}
                  </div>
                  <div class="divider">
                    <div class="divider-content"><h4>Work Assignments</h4></div>
                  </div>
                  <!-- <legend>Work Assignments</legend> -->
                  <div id="id_{{ work_assignment_inlineformset.prefix }}">
                  {% crispy work_assignment_inlineformset work_assignment_inlineformset.form.helper %}
                  </div>
                  <div class="form-group">
                    <input type="submit" name="save" value="Save" class="btn btn-primary col-sm-offset-4" id="submit-id-save" />
                    <input type="reset" name="reset" value="Reset" class="btn btn-inverse" id="reset-id-reset" />
                  </div>
                </form>
              </div>
            </div>
          </div>
{% endblock content_main %}
{% block footer_javascript %}
    {{ block.super }}
    <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
    <script src="{% static 'js/jquery.formset.js' %}"></script>
    <script type="text/javascript">
        $(function() {
            $('#id_{{ qualification_inlineformset.prefix }}').formset({
                prefix: "{{ qualification_inlineformset.prefix }}",
                formCssClass: "{{ qualification_inlineformset.prefix }}",
                addText: 'Add another',
                deleteText: 'Remove',
                addCssClass: 'add-qualification label label-success col-sm-offset-4',
                deleteCssClass: 'delete-qualification label arrow-up arrow-primary col-sm-offset-4'
            })
        });
    </script>
    <script type="text/javascript">
        $(function() {
            $('#id_{{ work_assignment_inlineformset.prefix }}').formset({
                prefix: "{{ work_assignment_inlineformset.prefix }}",
                formCssClass: "{{ work_assignment_inlineformset.prefix }}",
                addText: 'Add another',
                deleteText: 'Remove',
                addCssClass: 'add-work-assignment label label-success col-sm-offset-4',
                deleteCssClass: 'delete-work-assignment label arrow-up arrow-primary col-sm-offset-4'
            })
        });
    </script>
{% endblock footer_javascript %}

希望有人可以提供帮助。

谢谢。

2 个答案:

答案 0 :(得分:1)

表单集clean()方法中的逻辑不太正确。 super()方法的返回值不包含cleaned_data-该方法始终返回null。您需要分别检查每个表单的清理数据。像这样:

class QualificationCustomInlineFormSet(forms.BaseInlineFormSet):

    def clean(self):
        # The line below isn't going to work - you also don't need to call super().
        # cleaned_data = super().clean()
        for form in self.forms:
            # use form.cleaned_data
            qualification = form.cleaned_data.get('qualification', '').strip()
            if not qualification:
                msg = "Please enter qualification."
                raise forms.ValidationError(msg, "error")

答案 1 :(得分:0)

我刚刚设法找到解决问题的方法。原来,我不需要实现InlineFormSet clean方法。

单击提交时,FormView类仅验证在form_class FormView属性中指定的表单。它不会自动验证我添加的其他inlineformset。所以我要做的是验证视图的form_valid方法中的每个inlineformset

views.py

class JobAddView(LoginRequiredMixin, SuccessMessageMixin, FormView):

    template_name = 'cepalco_website_admin/job_form.html'
    form_class = forms.JobForm
    success_url = reverse_lazy('cepalco_website_admin:home')
    success_message = "Successfully added %(job_title)s!"
    head_title = "Add new job"
    title_text = head_title
    description = "Enter the following job information"
    QualificationInlineFormSet = inlineformset_factory(
        Job, Qualification,
        form=forms.QualificationForm,
        extra=0, can_delete=False, min_num=1
    )
    WorkAssignmentInlineFormSet = inlineformset_factory(
        Job, WorkAssignment, form=forms.WorkAssignmentForm,
        extra=0, can_delete=False, min_num=1
    )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['head_title'] = self.head_title
        context['title_text'] = self.title_text
        context['description'] = self.description
        context['qualification_inlineformset'] = self.QualificationInlineFormSet
        context['work_assignment_inlineformset'] = self.WorkAssignmentInlineFormSet
        return context

    def form_valid(self, form):
        qualification_formset = self.QualificationInlineFormSet(self.request.POST)
        if qualification_formset.is_valid():
            return super().form_valid(form)

        return render(self.request, self.template_name, {
            'form': form,
            'head_title': self.head_title,
            'title_text': self.title_text,
            'description': self.description,
            'qualification_inlineformset': qualification_formset,
            'work_assignment_inlineformset': self.WorkAssignmentInlineFormSet
        })

因此,即使不添加自定义inlineformset clean方法,该代码仍会验证表单集是否为空,并会提供一条错误消息,指出必须填写此字段。

希望这也可以帮助其他遇到相同问题的人。