Skip to main content

Make emailing users easy and entity-based.

Project description

https://travis-ci.org/ambitioninc/django-entity-emailer.svg?branch=develop

Django Entity Emailer

Do you:

  • Use Django-Entity?

  • Want to send emails to entities easily?

  • Want a record of emails sent?

  • Want automatic assurance that you don’t accidentally send hundreds of emails over the course of a few minutes?

Then use Django Entity Emailer!

Installation

This package can currently be installed by downloading and installing from source:

git clone python setup.py install

Coming soon: pip install.

Setup and Configuration

In order to use django-entity-emailer, you must be mirroring entities using the Django-Entity framework. Additionally, in order to send email to entities, those entities must include a value for the key 'email' in their entity_meta field.

If both of those conditions are true, setup is fairly straightforward:

  1. Add entity_emailer to INSTALLED_APPS.

  2. Either set a value for settings.ENTITY_EMAILER_FROM_EMAIL, or be sure that the settings.DEFAULT_FROM_EMAIL is set to an appropriate value.

  3. Ensure that all the dependencies are installed and listed in INSTALLED_APPS

    • pip: django-db-mutex, INSTALLED_APPS: db_mutex

    • pip: django-entity-subscription, INSTALLED_APPS: entity_subscription

  4. Add the scheduled email task to your CELERYBEAT_SCHEDULE (see configuring celery section).

  5. Run python manage.py syncdb and python manage.py migrate

  6. Ensure that a email medium is set up by running python manage.py add_email_medium.

When sending an email, django-entity-emailer will first check if the ENTITY_EMAILER_FROM_EMAIL exists. If it does, it will use that value in the email’s ‘from’ field, otherwise it will fall back to the value set in DEFAULT_FROM_EMAIL.

Finally, django-entity-emailer uses django-entity-subscription for subscription management. This libary makes it easy for developers and users to manage what sorts of notifications users recieve. However, it does require some configuration. For a simple emailer configuration, see the ‘Basic entity-subscription configuration’ section.

Getting 'email' into 'entity_meta'

The requirement that entities be mirrored with an 'email' field in their entity_meta is not difficult.

After installing django-entity, it is as simple as creating a model inheriting from entity.BaseEntityModel, with a get_entity_meta that returns the email along with any other data to be mirrored. A simple example could be:

from django.db import models
from entity import BaseEntityModel

class Account(BaseEntityModel)
    username = models.CharField(max_length=64)
    email = models.CharField(max_length=254)

    def get_entity_meta(self):
        return {'email': self.email, 'username': self.username}

Also note that it is not necessary for every mirrored entity to include an email, only those entities that will actually be sent emails need to have emails mirrored in their entity_meta.

For a more complete description of how entity mirroring works, see the documentation for django-entity.

Configuring Celery

To use the email scheduling feature of entity_emailer, you must add the appropriate task to your CELERYBEAT_SCHEDULE. For a general introduction to configuring periodic celery tasks in Django, see the official guide, Celery Periodic Tasks.

In short, it should be enough to add the following to your existing celerybeat schedule.

CELERYBEAT_SCHEDULE = {
    # ...
    'send_scheduled_emails': {
        'task': 'entity_emailer.tasks.SendUnsentScheduledEmails',
        'schedule': timedelta(minutes=1),
    },
    # ...
}

Making sure to use a value for 'schedule' that is appropriate for the volume of emails, and server resources.

Basic entity-subscription configuration

In order to ensure that users of your site will not recieve emails that they don’t want to recieve, the entity-emailer application ties in to the entity-subscription framework. As a developer it is up to you to expose the ability for users to subscribe and unsubscribe from emails. Here, we will show the basic configuration required to start sending emails.

Running manage.py add_email_medium will add the medium that entity-emailer relies on to send emails. We must also have a source of emails, and a subscription to that combination of email and source.

from entity_emailer import get_medium
from entity_subscription.models import Source, Subscription
from entity import Entity
from django.contrib.contenttypes.models import ContentType

super_entity=Entity.objects.get_for_obj(my_group_object)
user_entity_type = ContentTypes.objects.get_for_model(MyUserModel)

email_medium = get_medium()
admin_source = Source.objects.create(
    name='admin', display_name='Admin Notifications',
    description='Important notifications for the site Admin.',
)
Subscription.objects.create(
    source=admin_source, medium=email_medium,
    entity=super_entity, subentity_type=user_entity_type
)

Send an Email Immediately

Sending an email immediately is as simple as saving a record to the database. Django-entity-emailer listens to the post-save signal sent for the Email model and spawns a celery task to send the email asynchronously.

A prerequisite to sending an email is categorizing it into a source. Categorizing emails into sources makes it easier to allow users to unsubscribe from types of emails they don’t wish to receive. We have set up a source above, called admin_source, for the examples below, we will be using a source called marketing_source.

Before we can send an email, we also need to create an EmailTemplate for the context of our email to fill in. An email template is simply a reference to a django template to be filled in with some context.

This object can use a path that Django’s template loaders will understand, or store the template directly as a TextField. Here, we’re storing a simple text template. The different possibilities for constructing an EmailTemplate object are discussed more deeply in the “Email Templates” section.

new_item_template = EmailTemplate.objects.create(
    template_name='simple item email',
    template_text='Check out {{ item }} for the price of {{ value }}!'
)

Once an email type and template have been created, sending an email is as simple as creating an email field without specifying a scheduled field.

send_to_entity = Entity.objects.get_for_obj(some_user_with_an_email)

Email.objects.create(
    source=marketing_source,
    send_to=send_to_entity,
    subject='This is a great offer!',
    template=new_item_template,
    context={'item': 'new car', 'value': '$35,000'}
)

By saving this field, an email will be sent to the email stored in send_to_entity.entity_meta['email'].

Email an individual

As seen in the example above, emailing an individual is as simple as specifying the appropriate entity in the Email.send_to field. Additionally, because django-entity supports super-entity and sub-entity relationships, it is very easy to send emails to groups of individuals.

Email a group

Emailing all the users in a group comes nearly for free if the group is correctly mirrored in django-entity. Sending the email is still as simple as saving an instance of Email.

There are two changes we make from the example for sending to an individual.

First, the sent_to field is still an entity, but instead of an entity with an entity_meta['email'] value, it should be an entity that has a super-entity relationship to the entities the emails are to be sent to.

Second, a subentity_type field specifies what type of subentity we want to email All sub-entities of the send_to entity and of the type specified by subentity_type must have an ‘email’ set in their entity_meta.

A complete example is below:

from entity_emailer.models import Email

from entity.models import Entity
from django.contrib.contenttypes import ContentType

from my_example_app.models import Newsletter, NewsletterSubscribers

# This send_to_entity has sub-entities we want to send to.
marketing_news_today = Newsletter.objects.get(name='Marketing News Today')
send_to_entity = Entity.objects.get_for_obj(marketing_news_today)

Email.objects.create(
    source=marketing_source,
    # our send_to_entity, is a newsletter, a super-entity of
    # NewsletterSubscribers
    send_to=send_to_entity,
    # Below is our subentity type, NewsletterSubscribers
    subentity_type=ContentType.objects.get_for_model(NewsletterSubscribers)
    subject='This is a great offer!',
    template=new_item_template,
    context={'item': 'new car', 'value': '$35,000'}
)

Once this email is saved to the database, the email will be sent to all of the sub-entities of the marketing_news_today entity automatically.

This allows you to email any group of users that exists in your django application without having to write custom ORM queries to pull that group out of the database and organize their email addresses.

Send An Email at a Scheduled Time

Sending an email at a scheduled time is just as easy as sending one immediately. Assuming that the CELERYBEAT_SCHEDULE is correctly configured, as described in the “Setup and Configuration” section, the only difference from the process described above is that you must provide a value for the scheduled field.

from datetime import datetime

Email.objects.create(
    source=marketing_source,
    send_to=send_to_entity,
    subentity_type=ContentType.objects.get_for_model(NewsletterSubscribers)
    subject='This is a great offer!',
    template=new_item_template,
    context={'item': 'New Hoverboard', 'value': '$35,000'}
    scheduled=datetime(year=2022, month=01, day=01, hour=12),
)

The email created above will be sent at the time in the scheduled field, UTC.

Additionally, scheduled emails that are processed at the same time will re-use a connection to the SMTP server to minimize overhead.

Unsubscribing

Users may want to be able to unsubscribe from certain types of emails. This is easy in django-entity-emailer. Emails can be unsubscribed from by individual sources, by using the entity-subscription framework.

from entity_emailer import get_medium
from entity_subscription import Source, Unsubscribe

admin_emails = Source.objects.get(name='admin')
Unsubscribe.objects.create(
    entity=entity_of_user_to_unsub,
    source=admin_emails
    medium=get_medium()
)

This user will be excluded both from receiving emails of this type that were sent to them individually, or as part of a group email.

Email Templates

Instance of EmailTemplate are used to store email templates that can be re-used with different contexts.

The possible fields on EmailTemplate are:

  • template_name - Required. A descriptive name for the template.

  • text_template_path - A path to a template for a text email.

  • html_template_path - A path to a template for an html email.

  • text_template - A TextField for inputing a text email template directly.

  • html_template - A TextField for inputing an html email template directly.

Both a text and html template may be provided, either through a path to the template, or a raw template object. However, for either text or html templates, both a path and raw template should not be provided.

If all of text_template_path, text_template, html_template_path, and html_template are missing, if text_template_path and text_template are both provided, or if html_template_path and html_template are both provided, a ValidationError will be raised.

The email sending task will take care of rendering the template, and creating a text or text/html message based on the rendered template.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-entity-emailer-0.3.2.tar.gz (20.8 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page