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.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.
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.
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=...)
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.