This is an extendend version of the Rails Flash implementation by Sean Patrick Hogan that supports different message types.
Setting a flash message:
request.flash.error = 'Item could not be saved'
request.flash['error'] = 'Item could not be saved'
request.flash['foo'] = 'bar'
Displaying a flash in the view:
<!-- show the error message -->
{% if flash.error %}An error occured:{{ flash.error }}{% endif %}
<!-- just show the first message found -->
{% if flash %}An error occured:{{ flash }}{% endif %}
<!-- show all messages -->
{% for msg in flash %}{{ msg.type }}: {{ msg.msg }}{% endfor %}
Note that it still works with simple strings as well. Feel free to just use it like this:
request.flash = "Message"
And:
{% if flash %}{{ flash }}{% endif %}
However, be aware that once you did this, you destroyed the Flash() dict and thus lost the extended functionality.
You can use request.flash.clear() to remove all messages.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # based on the implementation by Sean Patrick Hogan: http://code.djangoproject.net/ticket/4604
class Flash(dict):
# make two commonly used flashes available as attributes
error = property(lambda s: s.get('error', ""), lambda s, v: s.update({'error': v}))
notice = property(lambda s: s.get('notice', ""), lambda s, v: s.update({'notice': v}))
# if used as a string, return the first message found
def __str__(self):
if len(self) > 0:
return self.values()[0]
return ""
# allow {% for msg in flash %}{{ msg.type }}: {{ msg.msg }}{% endfor %}
# this may not be necessary in newer versions of django where you should be
# able to do: {% for key, value in flash %}{{ key }}: {{ value }}{% endfor %}
def __iter__(self):
for item in self.keys():
yield {'type': item, 'msg': self[item]}
# evaluate to True if there is at least one non-empty message
def __nonzero__(self):
return len(str(self)) > 0
# Add to MIDDLEWARE_CLASSES
class Middleware:
def process_response(self, request, response):
try:
# make sure we never override an existing flash with an empty one.
# otherwise non-pageview requests (like views.static.serve) will
# override a previously set flash with the empty object created in
# process_request(). Note that we use len(), so it is still possible
# to clear a flash by setting the dict-item to ""
if len(request.flash):
request.session['flash'] = request.flash
except:
pass
return response
def process_request(self, request):
# Initialize a Flash dict that can be used in views
request.flash = Flash()
# Add to TEMPLATE_CONTEXT_PROCESSORS
def context_processor(request):
if 'flash' in request.session:
flash = request.session['flash']
del request.session['flash']
return {'flash': flash}
return {'flash': None}
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Hello,
I just found an issue. It doesn't work unless I redirect the user after I set the flash message. In other works it seems that the session value that is set is not accessible in the same request. For example: if a user submits a form and I want to display a flash message it doesn't get displayed unless I refresh the browser.
Any thoughts?
Thanks,
Sebastian Macias
#
Sebastian,
you're right - I never used it that way? How does Rails even work win that regard?
Anyway, I am not 100% sure of the implication or possible side effects of this, but I think it should work just fine. Try this context processor instead:
#
Hm, that should read "How does Rails work in that regard" - just were are my glasses? ;)
#
Rails works by doing:
flash.now[:notice] = 'Message'
Basically, it (this snippet) doesn't work (for this case) because the response is being processed after the view is. So, you've done your view and Django has the page rendered and it is at that point, right before sending it to the browser, that it calls process_response. So, there is nothing in the session's flash when the context processor is being called unless you've redirected.
But this can be fixed (and rather easily). Since we aren't redirecting in this scenario, we don't even have to bother storing it at all! Everything is resident in memory for the entire time we need it!
So, if we do something like:
request.flash['now'] = 'Heya!'
we have something that differentiates it - a "now" case.
Now, the context processor can say: hi! Is there a flash in this user's session? If so, I want to display it. Well, maybe there's a "now" flash that is in the request object itself that I want to display if there is no session flash.
That would look like this context processor:
What we've done is look to see if there is a session flash. Ok, there isn't. Then we look to see if there is a "now" flash. Yes! So, just put that in the view and delete it so that the process_response doesn't add it to the session for the next pageview.
Anyway, just ping me if this needs clarification.
#
Hello, this probably is a dumb question, but where do i put class Flash(dict)? Thanks!
#
ChloeO, put it in the same file as "class Middleware". As a matter of fact, you can put everything in one file (this is how I do it).
#
thanx for this RoR borrowing, pretty interesting
#
Please login first before commenting.