django-notification includes a class called ObservedItem, currently used in django-wiki to alert a user about an entry that they are watching, when it has been updated.
It is quite simple to re-use this in your own applications (I needed it for BadList [an ethical purchase informer]) like so:
Step 1: define some notices
So, if you don’t have one, create a management.py in your app and put something like the following in it:
from django.db.models import signals
from django.utils.translation import ugettext_noop as _
try:
from notification import models as notification
def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("watched_ingredient_updated",
_("Watched Ingredient Updated"),
_("an ingredient you are watching has been updated"))
signals.post_syncdb.connect(create_notice_types,
sender=notification)
except ImportError:
print "Skipping creation of NoticeTypes as notification app not found"
This adds a notice to the notifications database (when you next call python manage.py syncdb)
Step 2: let users ‘observe’ things
Basically you want to create a url-view pair that can retrieve an object (the one you want notifications about).
NB: you need to import notification into your views:
from notification import models as notification
lets say you have this in some a view intended for the URL /ingredients/observe/something/:
@login_required
def ing_watch(request, ingredient):
"""
Observe an Ingredient
"""
if request.user.is_authenticated:
i = get_object_or_404(Ingredient,name=ingredient)
This code should get the requested ingredient. The next line to add is:
notification.observe(i, request.user,'watched_ingredient_updated',signal='observe')
This makes a new ObservedItem object with the object instance set to ‘i’, attached to the user who requested the page, with the notice that we defined in management.py.
The signal is important: we override the default (which is ‘post_save’ if you dig around in the code) so that we can use different notifications for different things. The *normal* use for this is when an object is updated (in which case the ‘post_save’ makes sense). Indeed, that is pretty much all this example will do, but I show that it can be changed so that you may reuse it for other tasks (the reason I needed it to do this was when an object was related to by another object using a ForeignKey, in which case it is not the observed object which is updated, but another that only references it.)
Anyway…
Step 3: actually make a call to send out notices
So, in the case I describe above (where you only want this to happen after the object itsself has been updated) you can hook in the post_save signal to do the dirty work (assuming you leave ’signal’ as the default ‘post_save’):
At the end of your models.py (containing Ingredient):
if notification is not None:
signals.post_save.connect(notification.handle_observations, sender=Ingredient)
Otherwise, to use our custom signal ‘observe’, you can use the following code wherever you want to send the notice (e.g. after some form has been validated and saved):
notification.send_observation_notices_for(i, signal='observe')
where i is the Ingredient instance that you want to notify about (so you could get this from the ForeignKey field of some object you just saved that refers to it, or if you are using a form which updates/adds some Ingredient, then use the object returned from that)
Et Voila, how to quickly add in on-site and email notifications using ‘ObservedItem’ class of django-notification
Thanks to Brian Rosner for the help figuring this out.