Welcome to Django PyNotify’s documentation!¶
Django PyNotify¶
General purpose notification library for Django.
Free software: MIT license
Documentation: https://django-pynotify.readthedocs.io.
Supported Python versions: 3.7, 3.8, 3.9, 3.10, 3.11
Supported Django versions: 3.1, 3.2
Features¶
Easy integration into project
Notification templating and translation
Asynchronous operation
Credits¶
This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.
Installation¶
Stable release¶
To install PyNotify, run this command in your terminal:
$ pip install django-pynotify
This is the preferred method of installation, as it will always install the most recent stable release.
If you don’t have pip installed, this Python installation guide can guide you through the process.
From sources¶
The sources can be downloaded from the Github repo.
You can either clone the public repository:
$ git clone git://github.com/druids/django-pynotify
Or download the tarball:
$ curl -OL https://github.com/druids/django-pynotify/tarball/master
Once you have a copy of the source, you can install it with:
$ make install
Enable the library¶
Once installed, add the library to INSTALLED_APPS
in your Django project settings:
INSTALLED_APPS = [
...
'pynotify.apps.PyNotifyConfig'
]
Usage¶
Overview¶
The library uses following pipeline of components that handle creatation of notifications.
Signals¶
Notification creation process starts by sending a Django signal. This mechanism allows hooking notifications to various events within the system (even inside 3rd party libraries) and helps to decouple the code.
Following types of signals can be used:
internal Django signals, like
post_save
singals from 3rd party libraries, like
password_set
from django-allauthyour own signals
Imagine we would like to create a notification for an author, when someone reads his article. We start by defining a custom signal:
from django.dispatch import Signal
article_viewed = Signal(providing_args=['user', 'article'])
When this signal is sent, it means that user
has viewed article
.
Receivers¶
Receivers receive signals kwargs and pass them to handlers. There are currently two receivers implemented in the library:
By default, the library is using the synchronous receiver, because it is easier for use. The synchronous receiver calls directly the handler. Asynchronous receiver is more efficient, but requires additional setup, see Asynchronous operation.
You can set which receiver will be used in the pipeline by changing PYNOTIFY_RECEIVER
setting (see Configuration).
If you need to implement your own receiver, you should inherit from BaseReceiver
.
Most of the time, you don’t need to implement your own receivers.
Handlers¶
Handlers are probably the most important part of the pipeline. They handle creating of
Notification
model instance(s) from signal kwargs received from a receiver.
Handler is typically the only thing you need to define in order to create a new type of notification, provided the signal you want to react upon already exists.
Let’s create a simple handler for our “article viewed” notification:
from pynotify.handlers import BaseHandler
from articles.signals import article_viewed
class ArticleViewedHandler(BaseHandler):
def get_recipients(self):
return [self.signal_kwargs['article'].author]
def get_template_data(self):
return {
'title': 'Your article has been viewed!',
}
class Meta:
signal = article_viewed
As you can see, you need to implement at least following methods in the handler:
All handlers are typically kept in file handlers.py
in a dedicated application within the project. When handler is
defined (or more specifically, imported), it is paired with the signal defined in handler’s Meta
.
Let’s say you created notifications
app with notifications/handlers.py
. In order for handlers to be
automatically loaded, you must either set the PYNOTIFY_AUTOLOAD_MODULES
in project settings:
PYNOTIFY_AUTOLOAD_MODULES = ('notifications.handlers',)
or load handlers module manually when Django is ready, i.e. put following code to notifications/apps.py
:
from django.apps import AppConfig
class NotificationsConfig(AppConfig):
name = 'notifications'
verbose_name = 'Notifications'
def ready(self):
from . import handlers
Now, when you send the article_viewed
signal, a new notification will be created for article author.
Note
It is possible to set signal = None
in handler’s Meta
. In that case, the handler won’t be paired with any
signal and it’s up to you to call it directly. There are two use cases for this feature:
You want to use some custom signal mechanism, bypassing Django signals completely
You want to process created notifications outside of the handler (they are returned by handler’s
handle
method)
Templates¶
Templates are blueprints for notifications, they are referenced in the notification and are used to dynamically render
notification fields. Handler’s method get_template_data()
returns values for
NotificationTemplate
attributes.
When notification is being created, handler first checks if template with attributes returned by
get_template_data()
exists. If not, the template is first created and then assigned
to the created notification.
The most powerful feature of templates is probably the ability to dynamically render related objects. This can be best illustrated with an example. We will improve the “article viewed” notification from the previous section:
from pynotify.handlers import BaseHandler
from articles.signals import article_viewed
class ArticleViewedHandler(BaseHandler):
def get_recipients(self):
return [self.signal_kwargs['article'].author]
def get_template_data(self):
return {
'title': '<b>{{user}}</b> viewed your article {{article}}',
'trigger_action': '{{article.get_absolute_url}}'
}
def get_related_objects(self):
return {
'user': self.signal_kwargs['user'],
'article': self.signal_kwargs['article']
}
class Meta:
signal = article_viewed
As you can see, we have changed the template strings to true Django templates, because the template fields, when
accessed through Notification
, are rendered using Django template engine with context filled
with named related objects. This is very convenient since notifications will always stay up to date, even if related
object changes.
Note
For security reasons, you can only access related object’s string representation and a set of attributes defined in
PYNOTIFY_RELATED_OBJECTS_ALLOWED_ATTRIBUTES
. See Configuration for more information.
Caution
Avoid adding unnecessary attributes to PYNOTIFY_RELATED_OBJECTS_ALLOWED_ATTRIBUTES
, since it increases
coupling between notification template(s) and the code. This is undesirable and makes managing and maintenance of
notifications harder.
Always consider first to store attribute’s value in extra data (as described lower), or save nested objects as standalone related objects (if you really need dynamic behavior).
In case you want to “freeze” the values used in template strings (i.e. not reflect changes made in related objects),
define get_extra_data()
, which should return a dict of JSON serizalizable values.
These extra data are also put into template context, together with named related objects.
If you need some extra fields, that are relevant to your use case, you can pass extra_fields
, which is
expected to a be a flat dictionary of strings. These extra fields are also dynamically rendered, just like standard
notification fields.
Instead of using get_template_data()
, you can define handler’s attribute
template_slug
. This is a better option in case you prefer to often change template strings via administration
interface. Note, that the admin template (AdminNotificationTemplate
) referenced by slug
must already exist - it won’t be automatically created. You can create it in administration interface or using data
migration.
Given the admin template with slug article-viewed, our handler can be modified as follows:
from pynotify.handlers import BaseHandler
from articles.signals import article_viewed
class ArticleViewedHandler(BaseHandler):
template_slug = 'article-viewed'
def get_recipients(self):
return [self.signal_kwargs['article'].author]
def get_related_objects(self):
return {
'user': self.signal_kwargs['user'],
'article': self.signal_kwargs['article']
}
class Meta:
signal = article_viewed
Dispatchers¶
Dispatchers are used by handlers to propagate notifications through various communication channels, e.g. SMS, e-mails,
push. The library currently does not include any specific dispatchers, just the base class
BaseDispatcher
.
Let’s implement e-mail notifications for our “article viewed” notification. We’ll start by creating an e-mail dispatcher:
from pynotify.dispatchers import BaseDispatcher
from django.core.mail import send_mail
class EmailDispatcher(BaseDispatcher):
def dispatch(notification):
send_mail(
subject=notification.title,
message=notification.text,
from_email='noreply@example.com',
recipient_list=(notification.recipient.email,),
)
And now we will add our dispatcher to the handler:
from pynotify.handlers import BaseHandler
from articles.signals import article_viewed
from .dispatcher import EmailDispatcher
class ArticleViewedHandler(BaseHandler):
dispatchers = (EmailDispatcher,)
...
...
Extra tips¶
Simplified usage¶
If all you need is just to create notifications at some point in your code, you can start doing so right away after installation of the library. No further configuration needed!
To create a notification, simply call the notify()
function:
from django.contrib.auth.models import User
from pynotify.notify import notify
notify(recipients=User.object.all(), title='Hello World!')
Even with this simple approach, you can still use templating and/or translations.
Translations¶
Notification templates can be translated using the standard Django translation mechanism. The only thing needed is to
enable template translation by setting PYNOTIFY_TEMPLATE_TRANSLATE
to True
and include the translated messages
in *.po
files.
You can use gettext_noop()
when defining template strings, so the string will be automatically included in the
transaltion file(s):
def get_template_data(self):
return {
'title': gettext_noop('{{user}} viewed your article {{article}}'),
}
However keep in mind, that if you change the template string inside gettext_noop()
, you either have to change the
corresponding notification template saved in the database (e.g. using data migration) or keep the old string
in the translation file.
In case you are using template slugs, just put gettext_noop()
anywhere in the code and keep it in sync with
contents of the notification template saved in the database:
class ArticleViewedHandler(BaseHandler):
template_slug = 'article-viewed'
# title translation
gettext_noop('{{user}} viewed your article {{article}}')
Asynchronous operation¶
Creating notifications can be time demanding, especially when creating a lot of notifications at once or dispatching via 3rd party services (e.g. SMS, e-mails, push). Using the default synchronous operation in these cases considerably extends time needed to process a request. Therefore, it is recommended to always switch to asynchronous mode, if you can.
The library contains AsynchronousReceiver
, which allows asynchronous operation. Intead of
calling a handler directly, it works by passing serialized signal kwargs to a Celery task upon database transaction
commit and the task then calls the handler. Since serialization comes into play here, signal kwargs are restricted
to be either directly JSON serializable values or model instances (which are serialized using built-in
ModelSerializer
).
To go asynchronous, change setting PYNOTIFY_RECEIVER
to pynotify.receivers.AsynchronousReceiver
and start Celery
in your project in autodiscover mode. See http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html.
The Celery task is defined in the library, you don’t have to create one. But in case you want to use a custom Celery
task, set its import path to PYNOTIFY_CELERY_TASK
setting. Your custom task should grab all the arguments it
receives and pass them to process_task()
, like this:
from pynotify.helpers import process_task
@shared_task
def my_task(*args, **kwargs):
process_task(*args, **kwargs)
Configuration¶
You can configure the library in Django settings. Following options are available:
PYNOTIFY_AUTOLOAD_MODULES
(default:None
)Iterable of Python modules that contain notification handlers. These modules will be imported at startup, i.e. causing notification handlers to be automatically registered. For example, if you have Django app
notifications
with handlers stored inhandlers.py
, the module for autoload will benotifications.handlers
.PYNOTIFY_CELERY_TASK
(default:pynotify.tasks.notification_task
)Import path to a Celery task used in asynchronous mode. See Asynchronous operation.
PYNOTIFY_ENABLED
(default:True
)Boolean indicating if library functionality, i.e. creating of notifications, is enabled. More specifically, if set to
False
, receiver will not be called upon signal reception.PYNOTIFY_RECEIVER
(default:pynotify.receivers.SynchronousReceiver
)Import path to a receiver class.
PYNOTIFY_RELATED_OBJECTS_ALLOWED_ATTRIBUTES
(default:{'get_absolute_url', }
)A set of related object’s attributes that can be used in notification template(s).
PYNOTIFY_STRIP_HTML
(default:False
)If set to
True
, HTML tags and entities will be stripped off during notification rendering.PYNOTIFY_TEMPLATE_CHECK
(default:False
)Boolean indicating if template string should be checked before rendering. If any named related object or extra data used in the template string is missing,
MissingContextVariableError
will be raised.PYNOTIFY_TEMPLATE_PREFIX
(default:''
)String that is prepended to any template just before rendering. Can be used to load custom tags/filters.
PYNOTIFY_TEMPLATE_TRANSLATE
(default:False
)Boolean indicating if template string should be translated via
gettext()
before rendering.
Example project¶
You can quickly try the library in the included example project. Install the library for development, as described in Contributing and run following commands:
$ cd example
$ ./manage.py runserver
The example project will be available at https://localhost:8000/.
Asynchronous operation¶
If you want to try Asynchronous operation in the example project, make sure you have Redis installed and uncomment following
settings in example/config/settings.py
:
CELERY_BROKER_URL = 'redis://127.0.0.1'
PYNOTIFY_RECEIVER = 'pynotify.receivers.AsynchronousReceiver'
Then open a new terminal window and start Celery with:
$ cd example
$ celery -A config worker
Reference¶
pynotify.config¶
pynotify.dispatchers¶
pynotify.exceptions¶
pynotify.handlers¶
- class pynotify.handlers.BaseHandler[source]¶
Bases:
object
Base class for handling creation of notification(s). Its purpose is to process signal kwargs sent over a defined signal. There should be typically one handler (inherited from this class) for each signal. The handler must define inner class
Meta
with following supported attributes:signal
: Signal to which handler will be registeredallowed_senders
: Handler will be called only if signal was sent by allowed senderabstract
: If set to True, the handler will not be registered
- dispatcher_classes¶
An iterable of dispatcher classes that will be used to dispatch each notification.
- template_slug¶
Slug of an existing admin template to be used. If not defined, you must define
get_template_data()
method.
- get_dispatcher_classes()[source]¶
Returns iterable of dispatcher classes used to dispatch notification(s).
- get_extra_data()[source]¶
Returns a dictionary with extra data, the values must be JSON serializable.
Returns a list or dictionary of related objects in format {“name”: object}. Named related objects (i.e. those passed using a dictionary) can be referred in notification template.
pynotify.helpers¶
- class pynotify.helpers.DeletedRelatedObject[source]¶
Bases:
object
- Placeholder class that substitutes deleted related object and returns:
“[DELETED]” as its string representation
itself for any attribute accessed
- class pynotify.helpers.SecureRelatedObject(related_object)[source]¶
Bases:
object
Security proxy class allowing to access only string representation of the related object and a set of attributes defined in RELATED_OBJECTS_ALLOWED_ATTRS settings.
- pynotify.helpers.autoload()[source]¶
Attempts to load (import) notification handlers from modules defined in
PYNOTIFY_AUTOLOAD_MODULES
- pynotify.helpers.get_from_context(variable, context)[source]¶
Tries to find variable value in given context.
- Parameters
variable – Variable to look for. Template format is supported (e.g. “abc.def.ghi”).
context – Template context.
- Returns
Variable value or None if not found.
- pynotify.helpers.process_task(handler_class, serializer_class, signal_kwargs)[source]¶
Deserializes signal kwargs using the given serializer and calls given handler. This function is intended to be called from a Celery task.
- pynotify.helpers.receive(sender, **kwargs)[source]¶
Initiates processing of the signal by notification handlers through a receiver.
pynotify.models¶
- class pynotify.models.AdminNotificationTemplate(*args, **kwargs)[source]¶
Bases:
BaseTemplate
Represents a “template of a template”. This model is intended to be managed from administration, hence its name. It is identified by slug, which can be used for notification creation. However, this template is never used to directly render a notification, but instead is used to create NotificationTemplate with same values.
- slug¶
Template slug, with which this template can be referred to.
- is_active¶
Flag that switches on/off creating notifications from this template.
- is_locked¶
Flag that switches on/off this template editing (for admin purposes, requires admin-side support).
- send_push¶
Flag that switches on/off sending push notifications from this template. Currently, it has no effect on its own, but you can use it in your custom push notification solution.
- exception DoesNotExist¶
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned¶
Bases:
MultipleObjectsReturned
- class pynotify.models.BaseModel(*args, **kwargs)[source]¶
Bases:
SmartModel
Base class for models that outpus its verbose name and PK.
- class pynotify.models.BaseTemplate(*args, **kwargs)[source]¶
Bases:
BaseModel
Base abstract model for notification template.
- title¶
Title of the notification.
- text¶
Text of the notification.
- trigger_action¶
Arbitrary action performed when user triggers (i.e. clicks/taps) the notification.
- extra_fields¶
Can be used to store additional fields needed in particular use case.
- class pynotify.models.Notification(*args, **kwargs)[source]¶
Bases:
BaseModel
Represents the notification.
Attributes specified in
TEMPLATE_FIELDS
are also available here, as generated properties, that are evaluated at runtime and will return rendered field from the associated template. By default, the context used for rendering is filled with named related objects and extra data, so they can be referenced in the template by their name/key.- recipient¶
Recipient of the notification.
- template¶
Template used to render generated notification fields.
- is_read¶
Boolean flag indicating that recipitent has seen the notification.
- is_triggered¶
Boolean flag indicating that recipient has triggered the notification (e.g. clicked/tapped)
- extra_data¶
JSON serialized dictionary with extra data.
- exception DoesNotExist¶
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned¶
Bases:
MultipleObjectsReturned
- clean()[source]¶
Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.
- property context¶
Returns context dictionary used for rendering the template.
Returns named related objects as a dictionary where key is name of the related object and value is the object itself. Related objects without name are skipped.
- class pynotify.models.NotificationMeta(name, bases, attrs)[source]¶
Bases:
SmartModelBase
,type
Creates property for each template field. The property returns rendered template.
- class pynotify.models.NotificationQuerySet(model=None, query=None, using=None, hints=None)[source]¶
Bases:
SmartQuerySet
- class pynotify.models.NotificationRelatedObject(*args, **kwargs)[source]¶
Bases:
BaseModel
Represents object related to a notification. This object can be then referenced in notification template fields by its name (if not None).
- name¶
String identificator of the object (for referencing in templates).
- notification¶
Related notification.
- content_object¶
The related object itself.
- exception DoesNotExist¶
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned¶
Bases:
MultipleObjectsReturned
- class pynotify.models.NotificationTemplate(*args, **kwargs)[source]¶
Bases:
BaseTemplate
Represents template that is used for rendering notification fields. Each field specified in
TEMPLATE_FIELDS
is a template string, that can be rendered using therender
method.- admin_template¶
Reference to admin template that was used to create this notification template.
- exception DoesNotExist¶
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned¶
Bases:
MultipleObjectsReturned
pynotify.notify¶
- class pynotify.notify.NotifyHandler[source]¶
Bases:
BaseHandler
Notification handler for the
notify
method.- get_dispatcher_classes()[source]¶
Returns iterable of dispatcher classes used to dispatch notification(s).
- get_extra_data()[source]¶
Returns a dictionary with extra data, the values must be JSON serializable.
Returns a list or dictionary of related objects in format {“name”: object}. Named related objects (i.e. those passed using a dictionary) can be referred in notification template.
pynotify.receivers¶
- class pynotify.receivers.AsynchronousReceiver(handler_class)[source]¶
Bases:
BaseReceiver
Signal receiver that calls notification handler asynchronously via Celery.
- class pynotify.receivers.BaseReceiver(handler_class)[source]¶
Bases:
object
Base class for receiving signals. Its purpose is to pass signal kwargs to the notification handler.
- class pynotify.receivers.SynchronousReceiver(handler_class)[source]¶
Bases:
BaseReceiver
Signal receiver that calls notification handler synchronously.
pynotify.serializers¶
- class pynotify.serializers.BaseSerializer[source]¶
Bases:
object
Base class for serializing/deserializing signal kwargs. Its puprose is to transform signal kwargs to be directly JSON serializable (for compatible types, see https://docs.python.org/3/library/json.html#py-to-json-table).
pynotify.tasks¶
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at https://github.com/druids/django-pynotify/issues.
If you are reporting a bug, please include:
Your operating system name and version.
Any details about your local setup that might be helpful in troubleshooting.
Detailed steps to reproduce the bug.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.
Implement Features¶
Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.
Write Documentation¶
Django PyNotify could always use more documentation, whether as part of the official Django PyNotify docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/druids/django-pynotify/issues.
If you are proposing a feature:
Explain in detail how it would work.
Keep the scope as narrow as possible, to make it easier to implement.
Remember that this is a volunteer-driven project, and that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up Django PyNotify for local development.
Fork the repo on GitHub.
Clone your fork locally:
$ git clone git@github.com:your_name_here/django-pynotify.git
Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:
$ mkvirtualenv django-pynotify $ cd django-pynotify/ $ make develop
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
To create migrations, run:
$ make makemigrations
To make translations, run:
$ make po
To compile translations, run:
$ make mo
When you’re done making changes, check that your changes pass flake8 and the tests:
$ make lint $ make test
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
The pull request should include tests.
If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. Also consider inplementing the new functionality in the example project.
The pull request should work for all supported Python versions, and for PyPy. Check https://travis-ci.org/druids/django-pynotify/pull_requests and make sure that the tests pass for all supported Python versions.
Tips¶
To run a subset of tests:
$ cd example
$ ./manage.py test tests.test_config
Deploying¶
A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:
$ bumpversion patch # possible: major / minor / patch
$ git push && git push --tags
$ make release
Travis will then deploy to PyPI if tests pass.
Credits¶
List of people who helped with development of this library. Feel free to add your name to the list, if you’ve made a contribution.
Development Lead¶
Ondřej Kulatý <kulaty.o@gmail.com>
Contributors¶
Luboš Mátl
Petr Olah
History¶
0.5.5 (2023-01-03)¶
Add support for Python 3.10 and 3.11
0.5.4 (2022-03-10)¶
Migrate CI from Travis to Github Actions
Fix lint errors
0.5.3 (2022-03-01)¶
Prevent duplicates of
NotificationTemplate
objects
0.5.2 (2022-02-21)¶
Allow handlers that are not paired with any signal
Return notifications created with
notify
helper function
0.5.1 (2022-01-20)¶
Use
JSONField
instead ofTextField
for JSON based fieldsAdd
extra_fields
toBaseTemplate
Drop Django 2.x support
0.4.6 (2021-08-31)¶
Add
is_locked
field toAdminNotificationTemplate
0.4.5 (2021-01-21)¶
Update dependencies
0.4.4 (2021-01-15)¶
Add support for Python 3.9
Add support for Django 3
Fix BS4 warning
0.4.3 (2020-12-16)¶
Fix translation file
0.4.2 (2020-12-11)¶
Add
send_push
flag toAdminNotificationTemplate
modelIgnore duplicit dispatcher classes in
BaseHandler
0.4.1 (2020-10-12)¶
Add
PYNOTIFY_STRIP_HTML
config option
0.4.0 (2020-08-12)¶
Removed support of Django 1.11, 2.0 and 2.1
Fixed library requirements
0.3.2 (2020-07-27)¶
Add
is_active
flag toAdminNotificationTemplate
model
0.3.1 (2020-06-12)¶
Improve template variable checking
Add new filter
filter_with_related_object
0.3.0 (2020-04-19)¶
Fix documentation
Change
PYNOTIFY_AUTOLOAD_APPS
toPYNOTIFY_AUTOLOAD_MODULES
, i.e. allow notification handlers to reside in arbitrary module
0.2.2 (2020-02-11)¶
Use Django JSON encoder for encoding extra data
0.2.1 (2020-02-11)¶
Fix failed PyPi upload
0.2.0 (2020-02-11)¶
Add admin templates
Limit usage of related objects in templates and add
PYNOTIFY_RELATED_OBJECTS_ALLOWED_ATTRIBUTES
settingShow placeholder text for deleted related objects
0.1.7 (2020-01-20)¶
Add support for Python 3.8 and Django 2.2
Fix generating of translations
Allow unnamed related objects to be passed in a list
0.1.6 (2019-04-16)¶
Add
PYNOTIFY_TEMPLATE_PREFIX
config optionAdd methods
get_template_slug()
andget_dispatcher_classes()
toBaseHandler
Add coveralls.io integration
0.1.5 (2019-04-12)¶
Add extra data to
Notification
model
0.1.4 (2019-04-08)¶
Add
_can_handle()
method toBaseHandler
Add
PYNOTIFY_ENABLED
setting
0.1.3 (2019-04-01)¶
Add
kwargs
to Notification manager’screate()
methodAdd
realted_objects_dict
property toNotification
model
0.1.2 (2019-03-20)¶
Remove automatic deploy to PyPi from Travis
0.1.1 (2019-03-20)¶
First release of the library