1. Introduction

djmail is a BSD Licensed, simple and nonobstructive django email middleware. djmail was created by Andrey Antukh <niwi@niwi.nz> and is maintened by David Barragán <bameda@dbarragan.com>

2. Why use djmail?

Because it:

  • Sends emails asynchronously without additional libraries.

  • Sends emails using celery tasks.

  • Can retry sending failed messages (with cron task or celery periodic task).

  • Can assign delivery priority.

  • Has a powerfull class to build emails from templates.

  • Works transparently (works as middleware for native django email backends)

2.1. How to install?

The simplest way to get djmail for your project, is installing it using pip:

pip install djmail

3. User guide

As I have mentioned previously, djmail works as a middleware.

  • It saves emails using django orm and track its status. If an exception is raised during sending, it marks the email as failed and stores the exception traceback for posterior analysis.

  • Exposes usefull commands and methods for retring to send failed messages.

  • Handles priority, with ability to delay sending of messages with lowest priorty to periodic task (with cron or celery).

To get djmail working on your project, you should configure it. An example setup:

Add it to the INSTALLED_APPS your Django settings:
INSTALLED_APPS = [
  ...,
  'djmail',
  ...,
]
Set the email backends in the settings.
EMAIL_BACKEND="djmail.backends.default.EmailBackend"
DJMAIL_REAL_BACKEND="django.core.mail.backends.console.EmailBackend"

This specifies: emails are managed by djmail default backend and actually sent using console based django builtin backend.

3.1. How to use?

djmail works as a middleware, and the simplest way to use it, is to not think, just send emails the way you did before.

3.2. Backends

djmail comes with three backends:

  • djmail.backends.default.EmailBackend
    send emails synchronously using real backend.

  • djmail.backends.async.EmailBackend
    send emails asynchronously using concurrent.futures.

  • djmail.backends.celery.EmailBackend
    send emails asynchronously using celery tasks.

3.3. Management Commands

djmail has two built-in management commands:

  • djmail_retry_send_messages
    Sends all mail in the queue, also retrying previously failed email messages. After the global retry limit is reached, an email is marked as discarded.

  • djmail_delete_old_messages --days=183
    djmail stores all messages in the database, which could eventually take up a lot of database space for data which is not important to keep. This command deletes all succesfully sent messages older than the provided age (when the --days parameter is ommited, the default value is 6 months).

An example setup using cronjobs (sudo crontab -e) would be

@hourly python manage.py djmail_retry_send_messages
@weekly python manage.py djmail_delete_old_messages --days=31

4. Template Emails

djmail comes with two util classes for easily building emails from templates:

  • djmail.template_mail.TemplateMail: low level interface.

  • djmail.template_mail.MagicMailBuilder: high level interface with some magic.

4.1. TemplateMail

This is a low level interface for buiding emails using templates. It allows to statically define email classes for posterior usage:

from djmail import template_mail
# Define a subclass of TemplateMail
class SomeTemplateEmail(template_mail.TemplateMail):
    name = "some_email"

# Create a instance
email = SomeTemplateEmail()

# Buld and sent message with specified context
email.send("to@example.com", {"template": "context"})

Also you can obtain a native django email instance from TemplateMail instance:

# Create a instance
template_email = SomeTemplateEmail()

# obtain a native django email object
email = template_email.make_email_object("to@example.com",
                                         {"template": "context"})
email.send()

TemplateMail default implementation searches these templates:

  • emails/some_email-body-html.html

  • emails/some_email-body-text.html

  • emails/some_email-subject.html

Note
Text version of email is ommited if template does not exists.

4.2. MagicMailBuilder

This is a more powerful way for building email messages from templates. Behind the scenes, it uses TemplateMail implementation but exposes a dynamic api that allows building of subclasses on demand.

Example that represents the same behavior as previous example using dynamic api of MagicMailBuilder
from djmail import template_mail
# Create MagicMailBuilder instance
mails = template_mail.MagicMailBuilder()

# Create a native email object.
# NOTE: The method name represents a email name.
email = mails.some_email("to@example.com", {"template": "context"})
email.send()

Additionally, instead of receiver email address you can pass a django model instance that represents a user (it should have "email" field for work):

class MyUser(models.Model):
    email = models.CharField(max_length=200)
    lang = models.CharField(max_length=200, default="es")
    # [...]

user = MyUser.objects.get(pk=1)
email = mails.some_email(user, {"template": "context"})

Magic builder is really magic, and if your user class has lang field, magic builder uses it to setup a correct user language for rendering email in user locale.

Note
Also, you can specify a custom "lang" on context for same purpose.

5. Settings

djmail exposes some additional settings for costumizing a great part of default behavior.

  • DJMAIL_REAL_BACKEND
    Indicates to djmail which django email backend to use for delivering email messages.
    Default: django.core.mail.backends.console.EmailBackend

  • DJMAIL_MAX_RETRY_NUMBER
    Set a default maximum retry number for delivering failed messages.
    Default: 3

  • DJMAIL_BODY_TEMPLATE_PROTOTYPE
    Prototype for making body template path.
    Default: emails/{name}-body-{type}.{ext}

  • DJMAIL_SUBJECT_TEMPLATE_PROTOTYPE
    Prototype for make subject template path.
    Default: emails/{name}-subject.{ext}

  • DJMAIL_TEMPLATE_EXTENSION
    Extension used for build a final path of email templates.
    Default: html

6. License

Copyright (c) 2013-2015 Andrey Antukh <niwi@niwi.nz>
Copyright (c) 2015-2020 David Barragan <bameda@dbarragan.com>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.