Conditional redirecting in Pyramid
Question
I am looking for a way to redirect users to different routes/templates that is compatible with using @view_config.
I have a function that reads in an uploaded file and attempts to create a new model based on the file content. I was wondering if there was a clean way that I can redirect the user to one of two urls, based on whether the creation of the new model succeeds or there is an error.
If the model creation is successful, I want to redirect the user to the model page. If there is an error, I want to redirect the user to an error page. However, am having trouble breaking out the original function (load_model)'s view_config when rendering the error page.
@view_config(renderer="error.mak")
@view_config(renderer="model.mak",
route_name='load_model_route')
def load_model(self):
...
model = Model.find_model(model_name)
if model:
#redirect to model_route
else:
#redirect to model_error_route
Each route would have a @view_config that binds it to a function.
Solution
What you are asking is not "How to redirect" but "How to change renderer in the view function". To answer quickly, I think you could use:
request.override_renderer = 'other-renderer.mak'
But I don't think it's a good idea. Here's the usual pattern that is used most of the time to handle form submission:
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.url import route_url
from your_app import Model, some_stuff, save_to_db
@view_config(route_name='new_model',
renderer='model/new.mak', request_method='GET')
def new(request):
"""Shows the empty form."""
return {'model': Model(), 'errors': {}}
@view_config(route_name='create_model',
renderer='model/new.mak', request_method='POST')
def create(request):
"""Receives submitted form."""
model = some_stuff()
if len(model.errors) = 0: # is valid
# do your stuff, then redirect
save_to_db(model)
return HTTPFound(route_url('show_model', request, model_id=model.id))
# is invalid
return {'model': model, 'errors': model.errors}
@view_config(route_name='show_model',
renderer='model/show.mak', request_method='GET')
def show(request):
"""Shows details of one model."""
model = Model.find(request.matchdict['model_id'])
if model is None:
return HTTPNotFound()
return {'model': model}
In short:
- You show an empty form when you have a
GET
on the route for a new model. - You handle the form submission (
POST
) in a different view function- If the data is valid, you do your stuff, then you redirect with HTTPFound
- If the data is invalid, you return a dict to show the form again, with errors this time
- You use the same renderer in the
GET
andPOST
, but the one in thePOST
is only used in case of invalid data (otherwise, you redirect). - You have another view function to show the created model.
OTHER TIPS
Antoine showed a more general solution, but here's an attempt to stick to your basic format.
The idea is that you want to do some processing and then redirect the user to either a success or a failure page. You can redirect to a failure page if you want to just the same way you redirect to a success page, but I'll show a different version where you just show the error page in load, but if load works you redirect to the model.
config.add_route('show_model', '/models/{id}')
config.add_route('load_model', '/load_model')
@view_config(route_name='load_model', renderer='error.mak')
def load_model(self):
# ...
model = Model.find_model(model_name)
if model:
return HTTPFound(self.request.route_url('show_model', id=model.id))
return {} # some dict of stuff required to render 'error.mak'
@view_config(route_name='show_model', renderer='model.mak')
def show_model(self):
id = request.matchdict['id']
model = Model.get_model_by_id(id)
# be aware model could fail to load in this new request
return {'model': model} # a dict of stuff required to render 'model.mak'