How can I redirect after POST in Pyramid?

dave picture dave · Apr 10, 2011 · Viewed 19.1k times · Source

I'm trying to have my form submit to a route which will validate the data then redirect back to the original route.

For example:

  • User loads the page website.com/post
  • Form POSTs the data to website.com/post-save
  • User gets redirected back to website.com/post

Pyramid is giving me some troubles doing this.

Here's my slimmed down views.py

def _get_link_form(post_data):
    """ Returns the initialised form object """

    return LinkForm(post_data)

def home_page(request):

    form = _get_link_form(request.POST)
    return {'form' : form}

def save_post(request):
    """ form data is submitted here """"

    form = _get_link_form(request.POST)

    if not form.validate():
        return home_page(request, form)

This is the code I've been playing around with. Not only does it not work, it also feels messy and hacked up. Surely there's a simpler way to 'redirect after-POST' in Pyramid?

Answer

Michael Merickel picture Michael Merickel · Apr 12, 2011

Your problem is most easily solved by simply POSTing to the same URL that your form is shown at, and simply redirecting the user away from the page when the POST is successful. That way until the form is successfully submitted you do not change URLs.

If you're just dying to POST to a different URL, then you need to save the data using sessions, since you're obviously handling the form data between requests.

Typically if you want to be able to handle errors in your forms you would use a session and flash messages. To do this you simply add a location for flash messages to appear in your base template and setup session support using something like pyramid_beaker.

Assuming your home page is setup at the 'home' named-route:

from pyramid.httpexceptions import HTTPFound

def myview(request):
    user = '<default user field value>'
    if 'submit' in request.POST:
        user = request.POST.get('user')
        # validate your form data
        if <form validates successfully>:
            request.session.flash('Form was submitted successfully.')

            url = request.route_url('home') 
            return HTTPFound(location=url)
    return {
        # globals for rendering your form
        'user': user,
    }

Notice how if the form fails to validate you use the same code you did to render the form originally, and only if it is successful do you redirect. This format can also handle populating the form with the values used in the submission, and default values.

You can loop through the flash messages in your template of choice using request.session.peek_flash() and request.session.pop_flash().

route_url supports mutating the query string on the generated url as well, if you want to flag your home page view to check the session data.

You can obviously just pass everything in the query string back to the home page, but that's a pretty big security vulnerability that sessions can help protect against.