This post originated from an RSS feed registered with Python Buzz
by Ian Bicking.
Original Post: Web Application Patterns: Status Notification
Feed Title: Ian Bicking
Feed URL: http://www.ianbicking.org/feeds/atom.xml
Feed Description: Thoughts on Python and Programming.
Here's a little pattern I use quite often in web applications. Like a
lot of patterns, it's not tied to any particular environment, or even
to any specific application. There's a lot of these kinds of patterns
out there that are specific to web applications (in addition to the standard patterns),
but they don't seem to be well collected, defined, or named...
Frequently you will need to present out-of-context information to the
user. For instance, in a webmail application when sending an email.
A page saying "email successfully sent" isn't very interesting.
Simply redirecting the user to the email index isn't very friendly
either: it's important to give some feedback when a user performs a
state-altering action. You want to predict what the user is likely to
be most interested in, and direct them there, but you also want to put
some notification about what just happened.
In whatever framework I am using (Webware, Zope, PHP), I'll usually
add something like this to the template header (Webware here):
It's important to reset the messages session variable only
when you actually display it, as the user may get redirected before
the template is rendered (especially if you use redirect after POST --
another pattern -- which works well with this technique).
If you haven't seen tal:block before, any tag prefixed with
tal: gets removed from the final ZPT output, and any
attributes of that tag are treated as TAL attributes. It saves a bit
of typing, and anyone using ZPT can use all the help they can get to
save typing.
People sometimes implement this in different ways. For instance, they
simply invoke the template passing in a message variable. So in a
webmail application, this might look like:
# send mail action:
invalid_message = validate_email(request) # return None if valid
if invalid_message:
return sendmail_form(message=invalid_message)
sendmail(user.email, request.to, request.subject, request.body)
return mail_index(message='Email to %s sent successfully' %
request.to)
Anyone who likes to use the back button knows what a pain this sort of
application is to use. A common navigation would be to arrive at the
index page after sending, view an email, and then you want to go back
to the index (using your back button). But you can't, because that
index was the result of a POST, and probably isn't cachable. To
reload it would resend the email. (In contrast: for the invalid case
passing in a variable works well)
A solution to this is to redirect the user after sending their email.
There's a few gotchas here relating to exactly what result code you
use (there's a whole set of 3xx
result codes), but that's another topic.
People sometimes start passing the messages in GET variables,
e.g. redirect('/index?message=Email+sent+successfully').
This is terribly awkard, and it's not worth it to go down this path.
Just use sessions, which are perfectly suited for this task. The GET
variable is also flawed because that message is forever tied to that
page, even after a reload, and even after the message is no longer
timely.
Once you integrate this into your templates, you'll find yourself
using these messages all the time. You can use it to add quick error
messages, where before you might have been too lazy, or just have used
some sort of generic "There was an error, hit the back button on your
browser and correct it" page. You can notify the user of all sorts of
little events which you might otherwise simply imply (e.g., "item
deleted from cart", "edits saved", and so on). These updates give the
user a clear idea of what they've done, what they need to do next
(because they usually are in the middle of some longer set of tasks
only they understand), and generally that their efforts are
successful.