How to raise a error inside form_valid method of a CreateView
Asked Answered
W

1

16

In a Django project, I have a view (cloud), type: CreateView. This view has a inlineformset_factory. It works. But, if i submit the form with a error (look at "messages.error" below), the page is redirected to project.get_absolute_url(). The problem is: the form content back empty. I know thats because the HttpResponseRedirect.

How can I change this without to break the form?

views.py

class cloud(CreateView):
    template_name = 'base/cloud.html'
    form_class = UserForm

    def get_context_data(self, **kwargs):
        context = super(cloud, self).get_context_data(**kwargs)
        project = get_object_or_404(Project, slug=self.kwargs['slug'])
        context['project'] = project
        if self.request.POST:
            context['formset'] = IdeaFormset(self.request.POST or None)
        else:
            context['formset'] = IdeaFormset()
        return context

    def form_valid(self, form, **kwargs):
        project = get_object_or_404(Project, slug=self.kwargs['slug'])
        context = self.get_context_data()
        formset = context['formset']

        if formset.is_valid():
            self.object = form.save()
            formset.instance = self.object #IdeaFormFormSet

            nouns = project.nouns().values_list('content', flat=True)
            verbs = project.verbs().values_list('content', flat=True)
            error = False
            for form in formset.forms: #For each Idea
                form.instance.project = project
                if form.instance.sentence:
                    sentence = form.instance.sentence
                    validate_noun = [word for word in sentence.lower().split() if word in nouns]
                    validate_verbs = [word for word in sentence.lower().split() if word in verbs]

                    if (len(validate_noun) < 1):
                        error = True
                        messages.error(self.request, u'No noun was inserted into the sentence.', 'danger')

                    if (len(validate_verbs) < 1):
                        error = True
                        messages.error(self.request, u'No verb was inserted into the sentence.', 'danger')

            if not error:
                formset.save()
                messages.success(self.request, u'Success!')
            return HttpResponseRedirect( project.get_absolute_url() )

        else:
            return self.render_to_response(self.get_context_data(form=form))

cloud.html

        <form role="form" method="post">
            {% csrf_token %}
            <legend>Ideas</legend>
            <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
            {% for idea_form in formset %}
                {# Include the hidden fields #}
                {% for hidden in idea_form.hidden_fields %}
                    {{ hidden }}
                {% endfor %}
                <div class="panel panel-default">
                    <div class="panel-heading" role="tab" id="heading_{{ forloop.counter }}">
                    <h4 class="panel-title">
                        <a {% if not forloop.first %} class="collapsed" {% endif %}
                            data-toggle="collapse" data-parent="#accordion" href="#collapse_{{ forloop.counter }}" aria-expanded="{{ forloop.first }}" aria-controls="collapse_{{ forloop.counter }}">
                            Idea #{{ forloop.counter }}
                        </a>
                    </h4>
                    </div>

                    <div id="collapse_{{ forloop.counter }}" class="panel-collapse collapse {% if forloop.first %} in {% endif %}" role="tabpanel" aria-labelledby="heading_{{ forloop.counter }}">
                        <div class="panel-body form-group" id="idea_{{ forloop.counter }}">
                            <div class="container-fluid">
                            {% for field in idea_form.visible_fields %}
                                <div class="row">
                                    {{ field.errors }}
                                    {{ field.label_tag }} {{ field }}
                                    <span class="help-block">{{ field.help_text }}</span>
                                </div>
                            {% endfor %}
                            </div> <!--container-fluid-->
                        </div>
                    </div>
                </div>
            {% endfor %}
            </div>

            <legend>User</legend>

            {% for field in form %}
            <div class="form-group has-error">
                {{ field.label_tag }} {{ field }}
                {% if field.help_text %}
                    <p class="help-inline"><small>{{ field.help_text }}</small></p>
                {% endif %}
                <div class="help-block with-errors">
                    <span class="help-block">
                        {% for error in  field.errors %}{{ error }}{% endfor %}
                    </span>
                </div>
            </div>
            {% endfor %}
            {{ formset.management_form }}

            <div class="form-actions">
                <button type="submit" class="btn btn-primary">Send!</button>
            </div>
        </form>
Willaims answered 16/12, 2015 at 18:44 Comment(2)
I think your return HttpResonseRedirect statement might indent one more level to cover only successful case. Also, unindent the return self.render_to_response and remove else statement and see if that works.Chalaza
One more thing is that sounds like you try to check form validation after form.is_valid is called. But I think it's better to do it in form.clean method.Chalaza
M
47

As a general rule, model validation should go into model fields validators or model's clean method. Form validation should go into form's clean or clean_<field> methods.

If you need to validate things in your view's form_valid, you can use form.add_error and then, instead of redirecting (or returning super(cloud, self).form_valid(form), which redirects anyways), you could return super(cloud, self).form_invalid(form).

Check: https://docs.djangoproject.com/en/3.1/ref/forms/api/#django.forms.Form.add_error

Marjoram answered 16/12, 2015 at 18:56 Comment(4)
I already tried the solution: return form_invalid. But it breaks my form. When again renders the form, the UserForm is not displayed. In its place a new instance of IdeaFormset is shown, being doubled.Willaims
Looks like you're also having issues with more than one form/formset in your view, let me suggest you a further reading #34251912Schnitzel
Wow, thank you x9999 times. I was returning form_valid and couldn't see errors and it also redirected.Ghirlandaio
Its a kind of throwback to the ABAP/4 way of doing things where validating at every stage of transaction was so easy. I needed a way to capture user input, compare with existing value and validate - and it took care of the requirement.Verada

© 2022 - 2024 — McMap. All rights reserved.