Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A better pattern for Django form logic?

The standard pattern for the view logic for Django forms is thus:

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

This is fine in simple cases but quite easily descends into a complex mass of nested IF statements if your application logic gets a bit more complex.

Can anyone share their own cleaner approaches that avoid nested IF's and logic that depends on fall-through cases?

I'd be particularly interested in answers that don't rely on additional 3rd party apps.

like image 207
Andy Baker Avatar asked May 03 '11 15:05

Andy Baker


2 Answers

One of the included class based views is FormView (documentation). The two main methods you'd be concerned with are form_valid and form_invalid.

from django.views.generic import FormView

from myapp.forms import MyForm

class MyView(FormView):

    template_name = 'edit_something.html'
    form_class = MyForm
    success_url = '/success/' # you should use `reverse`, but let's stay focused.

    def form_valid(self, form):
        """
        This is what's called when the form is valid.
        """

        return super(MyView, self).form_valid(form)

    def form_invalid(self, form):
        """
        This is what's called when the form is invalid.
        """

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

Alternatively you can override post, get or put methods and handle the form according to each type of request.

like image 56
Derek Reynolds Avatar answered Sep 20 '22 10:09

Derek Reynolds


This is shortest approach I found:

def contact(request):
    # if it's POST request it'll have data else it'll  be unbound
    form = ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

    return render_to_response('contact.html', { 'form': form, })

of course if you don't like nested if you could try reversing logic:

def contact(request):
    template = 'contact.html'
    if request.method != 'POST': # GET return:
        return render_to_response(template, {'form': ContactForm()})

    form = ContactForm(request.POST)
    if not form.is_valid(): # FAIL return:
        return render_to_response(template, {'form': form})

    # here logic if user posted form and it's clean:

    return HttpResponseRedirect('/thanks/')
like image 25
rombarcz Avatar answered Sep 21 '22 10:09

rombarcz