Non Form Errors for a Formset not Rendering with Django Crispy Forms
Asked Answered
C

2

7

I implement a custom clean method to validate my formset. I know there are error as I can print them to the console but these non_form_errors() are never rendered in my template. How can I render them?

template.html:

<form action="{% url 'databank:register' %}" method="post" enctype="multipart/form-data">
  {% csrf_token %}

  <div class="container">
    <div class="row" style="margin-top: 30px"> 
      <div class="col-md-10 col-md-offset-1">
        {{ dataset_form.media }}
        {% crispy dataset_form %}
          <div class="authorFormset">
            {% for form in formset %}
              {% crispy form helper %}
            {% endfor %}    
          </div>
        {% crispy terms_form %}
      </div>
    </div>
  </div>

  <div class="container">
    <div class="row">
      <div class="col-md-10 col-md-offset-1">
        <input type="submit" class="btn btn-lg btn-success" value="Submit">
      </div>
    </div>
</div>
{{ formset.management_form }}

forms.py:

class BaseAuthorFormset(BaseFormSet):
    def clean(self):   
        if any(self.errors):
            return
        roles = []
        for form in self.forms:
            contributor_role = form.cleaned_data['role']
            for x in contributor_role:
                if (x == "Depositor") and (x in roles):
                    raise forms.ValidationError("Only one Contributor may be marked Depositor.")
                roles.append(x)
            if "Depositor" not in roles:
                raise forms.ValidationError("You must designate one Contributor as Depositor.")

    def __init__(self, *args, **kwargs):
        super(BaseAuthorFormset, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

class AuthorForm(forms.Form):

    first_name = forms.CharField(
        max_length=96,
        required=True,
    )

    middle_initial = forms.CharField(
        max_length=96,
        required=False,
    )

    last_name = forms.CharField(
    max_length = 96,
    required = True,
    )

    email = forms.EmailField(
        required = True
    )

    role = forms.ModelMultipleChoiceField(
        queryset=AuthorRole.objects.filter(is_visible=True),
        widget=forms.CheckboxSelectMultiple,
        required = False,
    )

    affiliation = forms.CharField(
        max_length=512,
        required=True,
    )


AuthorFormset = formset_factory(AuthorForm, formset=BaseAuthorFormset, extra=1)

class AuthorFormHelper(FormHelper):

    def __init__(self, *args, **kwargs):
        super(AuthorFormHelper, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        FormHelper.form_tag = False
        FormHelper.disable_csrf = True
        FormHelper.formset_error_title = "Contributor Errors"
        self.layout = Layout(
            Div(
                Div( HTML("<h4>Contributor</h4>"),
                    Div(
                        Div('first_name', css_class='col-xs-4'),
                        Div('middle_initial', css_class='col-xs-4'),
                        Div('last_name', css_class='col-xs-4'),
                        css_class='row'
                    ),
                    Div(
                        Div('affiliation', css_class='col-xs-12 col-sm-6'),
                        Div('email', css_class='col-xs-12 col-sm-6'),
                        Div(
                            Field(
                                InlineCheckboxes('role'),
                            ), 
                            css_class='col-xs-12 col-sm-6'),
                        css_class='row'
                    ),
                ),
                css_class = 'add_author',
                id="authorFormDiv"
            ),
        )
Chace answered 1/4, 2015 at 20:51 Comment(2)
Errors show as expected if it's a 'default' error, as in an error in one of the fields but the errors I define in my custom clean method don't output anywhereChace
I tried adding cleaned_data = super(BaseAuthorFormset, self).clean() right below the clean method but still doesn't work....any ideas?? I'm really stuck!Chace
H
5

After a lot of investigations, I found the solution for render non_form_errors of crispy BaseInlineFormSet in template by native django-crispy's filter. Maybe it would be helpful for someone (it works at least for django-crispy-forms==1.5.2, Django==1.6):

{% load crispy_forms_filters %}
{% if formset.non_form_errors %}
    {{ formset|as_crispy_errors }}
{% endif %}
Husch answered 18/9, 2015 at 11:15 Comment(0)
C
1

Thank you to @Brandon for helping me figure this out!

In the view, formset.__dict__ can be used to print out the object itself to figure out where the errors are being stored. Doing that showed that the formset errors are stored using the _non_form_errors key. I then passed those errors to the template!

Chace answered 6/4, 2015 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.