A Note on Handling Form Posts in Pyramid

Recap: some background. Pyramid allows the programmer to declare a renderer that will be invoked when a dictionary is returned from a view callable. The rendereer will usually be some kind of template, and it will usually produce HTML that can be rendered in the browser.

Classically, browsers work in a strict call-response cycle. The browser makes a request and the server is responsible for making a valid response. And in web applications, the cycle looks like: browser makes a GET request. The response is a form that the user fills in. After it is filled in, the browser makes a POST request. Ideally, the server will process the form, change any stored information, and REDIRECT the browser to some page. (For more information on why the redirect is done, you can start at: http://en.wikipedia.org/wiki/Post/Redirect/Get>)

But, what happens if the POST data has an error in it. Somehow the original form has to be "re-painted" and sent back to the user. And, users get really cranky if the have to type in the whole thing again. What is a good way to do this in pyramid?

End of recap

Now, suppose some view function, say edit_view, is associated with a /edit URL, and that it has a template, say edit_template.pt. There are actually at least three ways to do this.

Using a Different View for the GET and POST with different URLS

Here, the form has an action attribute that points to a different URL than the form is invoked by. For example, the form might be created by URL /edit and handled by /post_edit. That is, the edit template will have something like <form action="post_edit" ... >

So, you will need two distinct views, and two assets, e.g. using imperative configuration, you will have something like (traversal and Page Templates):

    config.add_view(views.edit, name='/edit', renderer='templates/edit.pt')
    config.add_view(views.post_edit, name='/post_edit', 
        renderer='templates/edit.pt')

The views might look like:

    def edit(request):
        # initial contact from client, return a form.  As there is a
        # renderer, this is done by just returning a dictionary.
        mydict = some_code_to_build_a_dictionary_with_stuff_needed()
        # if this is a new item, the dictionary will typically have all
        # blank values.  If editing a pre-existing item, the persistence
        # engine will be called to fill in the current values.
        # err will be a key of mydict with value ''
        return mydict

    def post_edit(request):
        # user has filled in form.
        params = request.params
        errs = check_for_errors(params)
        if errs:
            mydict = code_to_build_needed_dictionary_filled_with(params, errs)
            # err will be filled in with errs now.
            return mydict
        process_and_store(params)
        return HTTPFound(location=...)

The key realization is that both edit and post_edit are handled by a single renderer. This can be done because result of a successful post is a HTTP redirection.

Different Views with GET and POST with Form Action with One URL

Here, the form has an action attribute that points to the same URL that the form is invoked by. For example, the form might be both created and handled by URL /edit. So, here the edit template will have something like <form action="edit" ... >

Pyramid allows the view discovery process to be influence by what are called view predicates. Among the predicates is request_method, so the assets could be defined as follows:

    config.add_view(views.edit, name='/edit', request_method='GET',
        renderer='templates/edit.pt')
    config.add_view(views.post_edit, name='/edit', request_method='POST',
        renderer='templates/edit.pt')
The view functions will be identical to ther previous case.

Single View with GET and POST with Form Action with One URL

I did not originate this idea, I think I saw it first from Michael Merickle.

As in the previous case, the form has an action attribute that points to the same URL that the form is invoked by. For example, the form might be both created and handled by URL /edit. So, here the edit template will have something like <form action="edit" ... >

So, in this case, only a single asset needs to be configured, for example:

    config.add_view(views.edit, name='/edit', renderer='templates/edit.pt')

The view function might look something like this:

    def edit_view(request):
        if request.method == 'GET':
            # initial contact from client, return a form.  As there is a
            # renderer, this is done by just returning a dictionary.
            mydict = some_code_to_build_a_dictionary_with_stuff_needed()
            # if this is a new item, the dictionary will typically have all
            # blank values.  If editing a pre-existing item, the persistence
            # engine will be called to fill in the current values.
            # err will be a key of mydict with value ''
            return mydict
        elif request.method == 'POST'
            # user has filled in form.
            params = request.params
            errs = check_for_errors(params)
            if errs:
                mydict = code_to_build_needed_dictionary_filled_with(params, errs)
                # err will be filled in with errs now.
                return mydict
            process_and_store(params)
            return HTTPFound(location=...)

So, Which to Use

Well, choose the one that works best for you. The key observation is that you do, however, want to use a single template as renderer for the the GET and the erroneous POST cases. Personally, I lean towards the last. It makes the view a bit more complicated, but I think that it is more locally explicit than the other options.