Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutiple forms with Class Based View. How to show errors at the same page?

views.py

from forms.py import PersonCreateForm

class PersonCreateView(CreateView):
    model = Person
    form_class = PersonCreateForm
    template_name = "my_app/create_person.html"

    def form_valid(self, form):
        self.object = form.save()
        return redirect('/homepage/')


class PeopleListView(ListView):
        [...]
        context.update({
            'task_form': TaskCreateForm(),

        return context

In my template I just add in action url which handle PersonCreateView.

<form action="{% url 'people_create' %}" method="post">{% csrf_token %}

When Form is valid then all data are saved without problems and it redirects me to '/homepage/. But when my form is invalid then it redirects me to to {% url 'people_create' %} and shows errors at /homepage/people_create/

How can I avoid that? I want all errors show at same page without redirect.

like image 280
HereHere Avatar asked Oct 20 '22 08:10

HereHere


1 Answers

Handle the form on the same view you build it, otherwise the page will change. You may mix django.views.generic.edit.ModelFormMixin into your PeopleListView so it has most of the features you need.

class PeopleListView(ModelFormMixin, ListView):
    success_url = '/homepage/'  # should use reverse() here

    def get_context_data(self, **kwargs):
        # only add the form if it is not already given to us
        if not 'task_form' in kwargs:
            kwargs['task_form'] = self.get_form()
        return super(PeopleListView, self).get_context_data(**kwargs)

    def post(self, request, *args, **kwargs):
        # ListView won't have a post method, we define one
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)   # default behavior will save and redirect
        else:
            return self.form_invalid(form) # default behavior has to be overridden (see below)

    def form_invalid(self, form):
        # Whatever you wanna do. This example simply reloads the list
        self.object_list = self.get_queryset()
        context = self.get_context_data(task_form=form)
        return self.render_to_response(context)

There, you have three code paths:

  • Initial display will load the listview as usual, only an empty form will be added to the context.
  • On submitting valid input, the form_valid method is invoked, which will redirect to /homepage/.
  • On submitting invalid input, our overridden form_invalid method is invoked, which will render the page normally, except the form will contain the validation errors.

You may make the whole thing a bit more staightforward using a cached property for the form, but then you'd start working against Django's shipped views instead of with it, and might as well just use the basic View class and implement all logic by yourself. I'd stick with Django's views, but ymmv.

like image 128
spectras Avatar answered Oct 22 '22 01:10

spectras