Notifications Middleware for Session-Backed 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
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
'''
simple middleware and context processor for session-based messaging with types
Heavily inspired by patches on ticket 4604. Differs in that in this a notification
has type.

Installation:
add notifications.NotificationMiddleware to MIDDLEWARE_CLASSES
and notifications.notifications to TEMPLATE_CONTEXT_PROCESSORS

That assumes notifications.py is on pythonpath. If notifications.py lives in
your project dir, prefix those with '(projectname).'

Example use:
request.notifications.create('Some bland information message.')
request.notifications.create('Some more exciting error message.', 'error')

Example template code:
{% if notifications %}
<ul id="notifications">
    {% for notification in notifications %}<li class="{{ notification.type }}">{{ notification.content }}</li>
    {% endfor %}
</ul>
{% endif %}

Example styling:
points to names of famfam icons and assumes structure like 
#notifications, #notifications li {
  list-style: none;
  list-style-type: none;
  margin-left: 0;
}
.information, .error, .success, .warning {
  margin-top: 0.4em;
  border: 1px solid #9bb8d9;
  padding: 6px 6px 6px 30px;
}
.information {
  background: #dedfff url('../icons/information.png') 6px 50% no-repeat;
  color: #406299;
}
.error {
  background: #ffe1da url('../icons/exclamation.png') 6px 50% no-repeat;
  border-color: #f34f4f;
  color: #be0b0b;
}
.success {
  background: #e2f9e3 url('../icons/accept.png') 6px 50% no-repeat;
  border-color: #9c9;
  color: #080;
}
.warning {
  background:#fff8bf url('../icons/error.png') 6px 50% no-repeat;
  border-color: #ffd324;
  color:#eb830c;
}
'''

from django.utils.encoding import StrAndUnicode

DEFAULT_TYPE = 'information'

class NotificationMiddleware:
    def process_request(self, request):
        request.__class__.notifications = Notifications(request.session)
        return None

class Notifications:
    def __init__(self, session):
        self.session = session
        self.SESSION_VARIABLE = '_notifications'

    def get(self):
            return self.session.get(self.SESSION_VARIABLE, [])
    
    def get_and_clear(self):
            return self.session.pop(self.SESSION_VARIABLE, [])
    
    def create(self, message, type=DEFAULT_TYPE):
        """creates a notification
        
        arguments:
        message: text content of notification
        type: optional type (defaults to the value of DEFAULT_TYPE)
        """
        notifications = self.session.get(self.SESSION_VARIABLE)
        if notifications is None:
            notifications = []
            self.session[self.SESSION_VARIABLE] = notifications
        notifications.append({'content': message, 'type': type})
        self.session.modified = True

def notifications(request):
    """
    Returns notifications for the session and the current user.

    Note that this processor is only useful to use explicity if you are not
    using the (enabled by default) auth processor, as it also provides the
    notifications (by calling this method).

    The notifications are lazy loaded, so no notifications are retreived and deleted
    unless requested from the template.

    Both contrib.session and contrib.auth are optional. If neither is provided,
    no 'notifications' variable will be added to the context.
    """
    if hasattr(request, 'session') or hasattr(request, 'user'):
        return {'notifications': LazyNotifications(request)}
    return {}

class LazyNotifications(StrAndUnicode):
    """
    A lazy proxy for session and authentication notifications.
    """
    def __init__(self, request):
        self.request = request

    def __iter__(self):
        return iter(self.notifications)

    def __len__(self):
        return len(self.notifications)

    def __nonzero__(self):
        return bool(self.notifications)

    def __unicode__(self):
        return unicode(self.notifications)

    def __getitem__(self, *args, **kwargs):
        return self.notifications.__getitem__(*args, **kwargs)

    def _get_notifications(self):
        if hasattr(self, '_notifications'):
            return self._notifications

        # First, retreive any notifications for the user.
        if hasattr(self.request, 'user') and \
           hasattr(self.request.user, 'get_and_delete_messages'):
            self._notifications = [{'content': message, 'type': DEFAULT_TYPE} for
                message in self.request.user.get_and_delete_messages()]
        else:
            self._notifications = []

        # Next, retrieve any notifications for the session.
        if hasattr(self.request, 'session'):
            self._notifications += self.request.notifications.get_and_clear()
        return self._notifications
    notifications = property(_get_notifications)

More like this

  1. load m2m fields objects by dirol 3 years, 10 months ago
  2. Template filter that divides a list into exact columns by davmuz 2 years, 3 months ago
  3. models.py with django_dag models for parts hierarchy by j_syk 2 years, 8 months ago
  4. List all errors in a form +bootstrap highlighting by ibrahimlawal 1 year, 7 months ago
  5. Ajax form with jQuery by Donn 5 years, 8 months ago

Comments

Akram (on September 23, 2008):

Great! I was just looking for passing error type with messages.. Thanks

BTW it is NotificationMiddleware not Notification

Installation: add notifications.NotificationMiddleware to MIDDLEWARE_CLASSES

#

tclineks (on October 26, 2008):

@Akram thanks, fixed that typo.

#

guettli (on January 6, 2009):

I don't understand this:

def process_request(self, request):
        request.__class__.notifications = Notifications(request.session)

Why do you add this to the class for every request? Why not request.notifications=...?

I guess this is not thread safe since all threads use the same request class.

#

michelts (on April 9, 2009):

Hi,

I think you need to call unicode on the following part:

notifications.append({'content': unicode(message), 'type': type})

This way, it can convert gettext string before adding it to the session.

Thanks!

#

xhenxhe (on September 11, 2009):

Has anyone had any issues with this. I'm getting weird results. Sometimes there is a long delay after setting the notification and actually having it displayed. It seems very random though.

#

(Forgotten your password?)