Extended rails flash implementation

 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

  1. Cookie based flash errors and notices (a la Rails) by alexk 5 years, 7 months ago
  2. Flash Message Template Tag by rtconner 6 years, 9 months ago
  3. load m2m fields objects by dirol 3 years, 10 months ago
  4. Notifications Middleware for Session-Backed Messages by tclineks 5 years, 8 months ago
  5. Yet another SQL debugging facility by miracle2k 6 years, 8 months ago

Comments

sebastianmacias (on July 23, 2007):

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

#

miracle2k (on July 24, 2007):

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:

def context_processor(request):
    # start with flash from current request and reset it (so it won't
    # be stored in the session)    
    flash = request.flash
    request.flash = Flash()
    # add flash from session (e.g. from before a redirect), if any
    if 'flash' in request.session:        
        flash.update(request.session['flash'])
        del request.session['flash']
    # make available in template
    return {'flash': flash}

#

miracle2k (on July 24, 2007):

Hm, that should read "How does Rails work in that regard" - just were are my glasses? ;)

#

sphogan (on October 18, 2007):

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:

if 'flash' in request.session:
    flash = request.session['flash']
    del request.session['flash']
    return {'flash': flash}
elif 'now' in request.flash:
    flash = request.flash['now']
    del request.flash['now']
    return {'flash': flash}
return {'flash': None}

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.

#

ChloeO (on November 4, 2007):

Hello, this probably is a dumb question, but where do i put class Flash(dict)? Thanks!

#

miracle2k (on November 10, 2007):

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

#

Romain Hardouin (on June 11, 2008):

thanx for this RoR borrowing, pretty interesting

#

(Forgotten your password?)