Package: django-entity-subscription
Django Entity Subscription uses the power of theDjango-Entityframework to make managing subscriptions easy and powerful, for you
and your users.OverviewThis django app, includes four models:SubscriptionUnsubscribeMediumSourceall available from within the packageentity_subscription.models.By creating objects in these models, you and the users of your
application can have fine grained control over how users are notified
and about what they are notified. Theentity_subscriptionapp is
agnostic about how users are actually notified about the subscriptions
that are set up, but is designed to be a dependency of whatever
notification system is put in place. It is assumed that the consumer
of this library already has some idea of what notifications are going
to be sent, and what delivery methods are going to be used to send the
notifications.Once subscription information is stored in those models, theentity_subscriptionapp also provides some methods to make it
easier to reason about and act on the stored subscriptions.Subscription.objects.mediums_subscribedSubscription.objects.is_subscribedSources and MediumsTheentity_subscriptionapp creates a strong boundary between the
“source” of notifications from within an application and the “medium”
used to deliver the notification to the users of the application.SourcesFrom a user of your application’s perspective a “source” is a category
of notifications that they can subscribe to, or unsubscribe from. It
could be something like “New Products” or “Important Site
Changes”.For any given source, users may want to receive notifications over
different mediums (like “Email”, “In-site”, “text-message”). By
dividing notifications into different sources, users can choose to
receive that type of notification over whatever medium they prefer.To achieve this within an application a “source” of notifications is
an object that describes where in the app a notification came
from. Pieces of code that originate events which lead to notifications
will want to own aSourceobject, or at least know the name of one,
so that they can clearly communicate thesourceof the
notifications they are originating.The actual source objects in the database are fairly simple, and are
created through the standardobjects.createinterface. They have a
unique identifier, a user friendly display name, and a longer form
description.fromentity_subscription.modelsimportSourceSource.objects.create(name='new_products',display_name='New Products',description='Tells you whenever a new item is available in the store.')MediumsFrom a user of your application’s perspective a “medium” is a way in
which they can be notified. Your site may support a variety of
different mediums for notifying your users of different
happenings. Examples could include Email, Text Messages, a News-Feed,
or a In-site Notification center.Users will likely want to receive notifications through some
combination of the available mediums, but only for certain categories
of notifications. They may want some notifications that they view as
somewhat important to go to their email, notifications that are very
important to go to email and text-message, and all the rest to go to
an in-site notification center. By distinguishing between mediums in a
subscription library, users can decide how each “source” of
notifications is delivered to them.The pieces of the application that handle actually sending
notifications will want to own aMediumobject that describes
them, or at least know the unique name of one. This enables the code
sending notifications to handle subscriptions appropriately.As with sources, The actual medium objects in the database are fairly
simple, and are created through the standardobjects.createinterface. They have a unique identifier, a user friendly display
name, and a longer form description.fromentity_subscription.modelsimportMediumMedium.objects.create(name='in_site',display_name='In Site',description='Notifications available in the Accounts/Notifications tab.')Source and Medium ConsiderationsBothSourceandMediumobjects can be effectively used as
static records that are setup as initial data for an application, or
as dynamic records that change as the various sources and mediums for
notification change. It is important to consider, however, that
excessively dynamic sources and mediums will make it difficult for
individual entities to control their subscriptions.Subscriptions and UnsubscribingEntities and groups of entities can be subscribed to
notifications. Once subscribed, individuals, mirrored as entities, can
choose to unsubscribe from notifications for a given source and
medium.SubscriptionsSubscriptions will most often be created by the application, for an
entire group of entities. In this case, all the entities will receive
the notification, unless they later opt out. Subscriptions can also be
created for an individual entity to receive a certain type of
notification, as an opt-in subscription.This library includes the tableSubscription, available fromentity_subscription.models.Subscription. Creating aSubscriptionobject is straightforward, assuming the relevantSourceandMediumobjects have been created (See “Sources and
Mediums” above), and the entities to be subscribed and their group are
appropriately mirrored. From there, we can use the standardobjects.createinterface.Given the sources and mediums created above, and a relationship
betweenMyUserandMyGroupin a given application, the
following is a subscription for all the users in a particular group:frommy_app.modelsimportMyUserfrommy_app.modelsimportMyGroupfromentity.modelsimportEntity,EntityKindfromentity_subscription.modelsimportSubscription,Source,Mediumsuper_entity=MyGroup.objects.get(name='product_group')Subscription.objects.create(medium=Medium.objects.get(name='in_site'),source=Source.objects.get(name='new_products'),entity=Entity.objects.get_for_obj(super_entity),subentity_kind=EntityKind.objects.get(name='myuser'))EachSubscriptionobject stored in the database only subscribes
the group of entities to a single combination of aSourceandMedium. To create subscriptions for delivering notifications from
the same source over different mediums, a newSubscriptionobject
must be created for each source/medium combination.  This allows the
application developer and the users to have detailed control over what
the users are notified about, and how those notifications are
delivered.UnsubscribingIndividual users of your application may wish to remove themselves
from receiving certain types of notifications.To unsubscribe an individual from from receiving notifications of a
given source/medium combination is as simple as creating anUnsubscribeobject. Assuming that “Robert” was subscribed to New
Product notifications in the subscription object above, unsubscribing
him from these notifications looks like:frommy_app.modelsimportMyUserfromentity.modelsimportEntityfromentity_subscription.modelsimportUnsubscribe,Source,MediumRobert=MyUser.objects.get(name='Robert')Unsubscribe.objects.create(entity=Entity.objects.get_for_obj(Robert)medium=Medium.objects.get(name='in_site')sorce=Source.objects.get(name='new_products'))With this object created, the rest of the group will receive these
notifications still, however “Robert” will no longer see them.Subscriptions and Unsubscribing ConsiderationsSeparating subscriptions and unsubscriptions into separate tables
allows for groups of entities to be subscribed with a single object in
theSubscriptiontable. This is useful for subscribing large
groups of users to a notification by default.If a given notification may only have a few users interested in
receiving, it may make more sense for it to be an opt-in notification,
where a Subscription object is made for each single entity that wishes
to opt in (that is, aSubscriptionobject with asubentity_kind=None). This may make more sense then subscribing
large groups to this notification and having most of them unsubscribe.Checking SubscriptionsOnce your sites subscriptions are stored as shown above, you will want
to use those subscriptions to decide to deliver (or not deliver)
notifications. Theentity_subscriptionapp provides a couple
methods to make it easier to find who is subscribed to what.TheSubscriptionManagerThe following methods are available from the manager of theSubscriptionmodel.mediums_subscribed(source, entity, subentity_kind=None)Return a queryset of all the mediums the givenentityis
subscribed to, for the givensource.If the optionalsubentity_kindparameter is given, returneverymedium that any of the givenentity’s sub-entities, of
the givenEntityKind, is subscribed to.is_subscribed(source, medium, entity, subentity_kind=None)Return a Boolean, indicating if the entity is subscribed to the
givensourceon the givenmedium.If the optionalsubentity_kindparameter is notNone,
returnTrueifanyof theentity’s sub-entities, of the
given type, are subscribed to the givensourceon the givenmedium.In the common case, checking for subscriptions involves looking at the
mediums a single entity is subscribed to. In this case bothmediums_subscribedandis_subscribedshould behave exactly as
expected. Their exact behavior is described in more detail below, in
the section “Checking if an individual entity is subscribed”.The implications of including asubentity_kindargument are
somewhat more subtle. These implications are described in more detail
below, in the section “Checking if anyone in a group is subscribed”.Checking if an individual entity is subscribedBefore sending notifications to users, your application wants to make
sure that it’s sending those notifications to users who have been
included through a subscription, and not excluded themselves by
unsubscribing.To check the subscription status of a single entity, simply callmediums_subscribedif you want a list of all the mediums an entity
is subscribed to, for a given source, or callis_subscribedif you
want to check if that entity is subscribed to a particular medium for
a given source. When checking the subscription status of a single
entity, thesubentity_kindargument should be left asNone.Whenmediums_subscribedoris_subscribedare called without asubentity_kindargument, the behavior of these methods is
straightforward. They will return a medium, or return true for that
medium, only if:1. The entity is part of a individual subscription, or is part of a
group subscription for the given source.The entity is not unsubscribed from that source and medium.Once you have checked that an individual entity is subscribed to a
given source/medium combination, you can be confident in delivering
that notification.Checking if anyone in a group is subscribedIn some cases, your application may have an event that applies to a
group of individuals, and you may wish to check if any of those
individuals are subscribed to receive notifications for that
event.Bothmediums_subscribedandis_subscribedcan also take an
optional parametersubentity_kindwhich will change their
behavior fairly significantly. In this case, the provided argument,entity, is assumed to be a super-entity, and these functions
return values based on whatanyof the sub entities are subscribed
to.So, passing in a super-entity, and subentity-type to eithermediums_subscribedoris_subscribedcan provide a useful start
for delivering notifications.Note that this is only anapproximationof what individuals in the
group are subscribed to. Before actually delivering a notification
to any subentity, the application must check that each user is
actually subscribed to receive that notification.Filtering entities based on subscriptionsGiven some number of entities, that may or may not be subscribed to
notifications from a given source and medium, it could be complicated
to determine all the possible entity relationships, and the relevant
subscriptions and unsubscriptions to check exactly which of those
entities should be notified. The entity-subscription framework
provides a method,Subscription.objects.filter_not_subscribedthat
will take the list of entities and return only those that should
definitely recieve the notification.entities=[entity_1,entity_2,entity_3]subscribed_entities=Subscription.objects.filter_not_subscribed(source,medium,entities)This method returns a queryset of entities to be notified. It takes
into account all possible group subscriptions the entities may belong
to, as well as removing entities that are unsubscribed from these
notifications.It does, require, however, that all the entities provided are of the
sameentity_kind.Release notes0.4.0Migrated Django Entity Subscription to use theEntityKindmodel for specifyingdifferent kinds of entities. This was a new addition in Django Entity 1.5. Schema migrations
are provided that remove thesubentity_typeContentTypevariable from theSubscriptionmodel and add thesubentity_kindEntityKindvariable. Note that it is up to the
user to write the appropriate data migration for converting entity types to entity kinds.