Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to django form processing boilerplate?

The suggested pattern for processing a form in a view seems overly complex and non-DRY to me:

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,
    })

That's a lot of conditionals, it repeats the ContactForm() construction, and the whole block is repeated everywhere a view needs to process a form. Isn't there a better way of doing it?

like image 828
John Avatar asked Jul 18 '09 21:07

John


2 Answers

You can avoid the repetition, of course. Mostly, you need to pass in as arguments the class of form and template name to use, a callable to process the cleaned data when a valid form is submitted, and a destination for the redirect after such processing; plus, you need a little extra code to call the form class just once, to produce either a bound or unbound form, and deal with it properly. I.e.:

def process_any_form(request, 
                     form_class, template_file_name,
                     process_data_callable, redirect_destination):

    form = form_class(request.POST if request.method == 'POST' else None)

    if form.is_bound and form.is_valid():
        process_data_callable(form.cleaned_data)
        return HttpResponseRedirect(redirect_destination)

    return render_to_response(template_file_name, {'form': form})
like image 177
Alex Martelli Avatar answered Sep 27 '22 15:09

Alex Martelli


You are right it could be better, here is a better alternative (but keep reading):

def contact(request):
     form = ContactForm(request.POST or None) # 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

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

This snippet comes from a talk called Advanced Django Form Usage from DjangoCon11.

Note that this will process an empty form as valid (even before submission) if all the fields are optional and you don't use CSRF protection. So to eliminate that risk, you better use this one:

    def contact(request):
     form = ContactForm(request.POST or None) # A form bound to the POST data
     if request.method == 'POST' and form.is_valid(): # All validation rules pass
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

     return render_to_response('contact.html', {
        'form': form,
     })
like image 28
Juande Carrion Avatar answered Sep 27 '22 16:09

Juande Carrion