Notifications¶
The notify
Function¶
With our listeners listening on our channels, all that remains is to define where our notifications are sent from.
For our first example, we need to send a notification through the PostReads
channel
whenever a Post
object is read. To achieve this, we can make use of the
pgpubsub.notify.notify
function.
In our example, we create a fetch
class method
on the Post
model which is used to retrieve a Post
instance from the database
and also send a notification via the PostReads
channel to asynchronously invoke the
update_post_reads_per_date_cache
listener. This fetch
method could then
of course be utilised in whatever API call is used when a user reads a post:
# tests/models.py
import datetime
from django.db import models
import pgpubsub
class Post(models.Model):
...
@classmethod
def fetch(cls, post_id):
post = cls.objects.get(pk=post_id)
pgpubsub.notify(
'pgpubsub.tests.channels.PostReads',
model_id=post_id,
date=datetime.date.today(),
)
return post
A few notes on the above implementation:
Under the hood, this python function is making use of the postgres
NOTIFY
command to send the payload as a JSON object.The first argument to the
notify
function can either be the full module path of a channel or the channel class itself. The following keyword arguments should match the dataclass fields of the channel we’re notifying (up to optional kwargs).Using
pgpubsub.notify.notify
is the appropriate choice for any non-postgres trigger based notification.
Trigger Notifications¶
For trigger based channels, notifications are sent at the database layer whenever the trigger is invoked. To understand this in a bit more detail, let’s consider our example above:
import datetime
import pgpubsub
from pgpubsub.tests.channels import AuthorTriggerChannel
from pgpubsub.tests.models import Author, Post
@pgpubsub.post_insert_listener(AuthorTriggerChannel)
def create_first_post_for_author(old: Author, new: Author):
print(f'Creating first post for {new.name}')
Post.objects.create(
author_id=new.pk,
content='Welcome! This is your first post',
date=datetime.date.today(),
)
As explained in the previous section, if we write this function and perform a migration, the post_insert_listener
decorator ensures that a trigger function
is written to the database. Then, after any Author
row is inserted to the
database, the post_insert_listener
also ensures that that database-level trigger
function is invoked, firing a notification with a JSON payload consisting
of the OLD
and NEW
values of the Author
instance before and after the
its creation. Associating the channel like so
post_insert_listener(AuthorTriggerChannel)
ensures that the notification is sent via the AuthorTriggerChannel
and hence ends up being
processed by the create_first_post_for_author
listener. To examine the internals of the trigger functions used to send notifications at the database level,
see pgpubsub.triggers.py
.
Note that postgres ensures that notifications sent via NOTIFY
are only sent after the commit which
created them is committed, we can be sure that in our example our newly
created Author
will be safely in the database before the listener process attempts to
associate a Post
to it.