First, add "mailer" to your INSTALLED_APPS
in your settings.py
:
In settings.py
:
INSTALLED_APPS = [
...
"mailer",
...
]
Run ./manage.py migrate
to install models.
This is the preferred and easiest way to use django-mailer.
To automatically switch all your mail to use django-mailer, set
EMAIL_BACKEND
:
EMAIL_BACKEND = "mailer.backend.DbBackend"
If you were previously using a non-default EMAIL_BACKEND
, you need to configure
the MAILER_EMAIL_BACKEND
setting, so that django-mailer knows how to actually send
the mail:
MAILER_EMAIL_BACKEND = "your.actual.EmailBackend"
For testing purposes, you could set this to
"django.core.mail.backends.console.EmailBackend"
to just print emails to the
console.
Now, just use the normal Django mail functions for sending email. These functions will store mail on a queue in the database, which must be sent as below.
As an alternative to the above, which dates from before there was such as thing
as an "email backend" in Django, you can import the send_mail
function (and
similar) from mailer
instead of from django.core.mail
. There is also a
send_html_mail
convenience function. However, we no longer guarantee that
these functions will have a 100% compatible signature with the Django version,
so we recommend you don't use these functions.
Having put mail on the queue, you need to arrange for the mail to be sent, which
can be done using the management commands that django-mailer
adds.
This is a management command that can be run as a scheduled task. It triggers
the send_all()
command, which sends all the mail on the queue.
If there are any failures, they will be marked deferred and will not be
attempted again by send_all()
.
This is an alternative to send_mail
, which keeps running and checks the
database for new messages every MAILER_EMPTY_QUEUE_SLEEP
(default: 30)
seconds. It should be used instead of send_mail
to circumvent the maximum
frequency of once per minute inherent to cron.
This is a more advanced alternative to send_mail
, for PostgreSQL only.
This process keeps running and checks the database for new messages every
MAILER_EMPTY_QUEUE_SLEEP
(default: 30) seconds. In addition, it uses
PostgreSQL’s NOTIFY/LISTEN pub-sub mechanism to send emails as soon
as they have been added to the database (and the transaction is committed).
Under the hood the command automatically adds a trigger to the Message
table
which sends a NOTIFY and then LISTENs on the same channel, using a single worker
thread to send emails. It uses the same send_all()
command internally as
other mechanisms.
To add rate controls, the MAILER_EMAIL_MAX_BATCH
setting mentioned below is
not very effective. While it is still honoured, a “batch” is now triggered
whenever new mail is put on the queue, rather than only after a scheduled delay.
This means you will need to use MAILER_EMAIL_THROTTLE
(see below) to limit
the number of emails sent.
This will move any deferred mail back into the normal queue, so it will be
attempted again on the next send_mail
. It should be run at regular period to
attempt to fix failures caused by network outages or other temporary problems.
This will remove old successful message logs from the database, to prevent it
from filling up your database. Use the -r failure
option to remove only
failed message logs instead, or -r all
to remove them all.
An example cron file looks like this:
* * * * * (/path/to/your/python /path/to/your/manage.py send_mail >> ~/cron_mail.log 2>&1) 0,20,40 * * * * (/path/to/your/python /path/to/your/manage.py retry_deferred >> ~/cron_mail_deferred.log 2>&1) 0 0 * * * (/path/to/your/python /path/to/your/manage.py purge_mail_log 7 >> ~/cron_mail_purge.log 2>&1)
For use in Pinax, for example, that might look like:
* * * * * (cd $PINAX; /usr/local/bin/python manage.py send_mail >> $PINAX/cron_mail.log 2>&1) 0,20,40 * * * * (cd $PINAX; /usr/local/bin/python manage.py retry_deferred >> $PINAX/cron_mail_deferred.log 2>&1) 0 0 * * * (cd $PINAX; /usr/local/bin/python manage.py purge_mail_log 7 >> $PINAX/cron_mail_purge.log 2>&1)
This attempts to send mail every minute with a retry on failure every 20 minutes, and purges the mail log for entries older than 7 days.
If you are using runmailer
or runmailer_pg
you don’t need the
send_mail
item.
If you are using runmailer
or runmailer_pg
instead of send_mail
,
it's up to you to keep this command running in the background, restarting it if
it crashes. This can be achieved using supervisord or similar software, such
as a systemd service unit file.
The send_all
command uses a filesystem-based lock file in case clearing the
queue takes longer than the interval between calling send_all()
. This works
to stop multiple workers on a single machine from processing the messages
multiple times.
To stop workers processes on different machines from sending the same mail multiple times, it also uses database-level locking where possible. Where available this is more reliable than filesystem-based locks.
If you need to be able to control where django-mailer puts its lock file, you
can set MAILER_LOCK_PATH
to a full absolute path to the file to be used as a
lock. The extension ".lock" will be added. The process running send_all()
needs to have permissions to create and delete this file, and others in the same
directory. With the default value of None
django-mailer will use a path in
current working directory.
If you want to disable the file-based locking, you can set the
MAILER_USE_FILE_LOCK
setting to False
.
If you wish to have a finer control over the delivery process, which defaults to deliver everything in the queue, you can use the following 3 settings:
MAILER_EMAIL_MAX_BATCH
: integer orNone
, defaults toNone
- how many emails are sent successfully before stopping the current run ofsend_all()
MAILER_EMAIL_MAX_DEFERRED
: integer orNone
, defaults toNone
- after how many failed/deferred emailssend_all()
should stop.MAILER_EMAIL_THROTTLE
: integer, defaults to 0 - how many seconds to sleep after sending an email.
If limited by MAILER_EMAIL_MAX_BATCH
or MAILER_EMAIL_MAX_DEFERRED
,
unprocessed emails will be evaluated in the following delivery iterations.
django-mailer comes with a default error handler
mailer.engine.handle_delivery_exception
.
It marks the related message as deferred for any of these exceptions:
smtplib.SMTPAuthenticationError
smtplib.SMTPDataError
smtplib.SMTPRecipientsRefused
smtplib.SMTPSenderRefused
socket.error
Any other exception is re-raised. This is done for backwards-compatibility as well as for flexibility: we would otherwise have to maintain an extensive and changing list of exception types, which does not scale, and you get the chance to do error handling that fits your needs.
When the default behavior does not fit your needs, you can specify your
own custom delivery error handler through setting MAILER_ERROR_HANDLER
.
The value should be a string for use with Django's import_string
,
the default is "mailer.engine.handle_delivery_exception"
.
Your handler is passed three arguments, in order:
connection
— the backend connection instance that failed deliverymessage
— theMessage
instance that failed deliveryexc
— the exception instance raised by the mailer backend
Your handler should return a 2-tuple of:
- a connection instance (or
None
to cause a new connection to be created) - a string denoting the action taken by the handler,
either
"sent"
or"deferred"
precisely
For an example of a custom error handler:
def my_handler(connection, message, exc): if isinstance(exc, SomeDeliveryException): # trying to re-send this very message desperately # (if you have good reason to) [..] status = 'sent' elif isinstance(exc, SomeOtherException): message.defer() connection = None # i.e. ask for a new connection status = 'deferred' else: raise exc return connection, status
If you need to change the batch size used by django-mailer to save messages in
mailer.backend.DbBackend
, you can set MAILER_MESSAGES_BATCH_SIZE
to a
value more suitable for you. This value, which defaults to None
, will be passed to
Django's bulk_create method
as the batch_size
parameter.
To limit the amount of times a deferred message is retried, you can set
MAILER_EMAIL_MAX_RETRIES
to an integer value. The default is None
, which means
that the message will be retried indefinitely. If you set this to a value of 0
,
the message will not be retried at all, any number greater than 0
will be the
maximum number of retries (excluding the initial attempt).
To not log the email contents after sending it, you can set MAILER_EMAIL_LOG_MESSAGE_DATA
to False. The default is True
, which means that the message will be stored with the full
email content. If you set this to the value False
, only the message meta data and result
status will be stored in the MessageLog
table.
Disabling storing the email content can be useful for privacy or performance reasons,
it also helps to not increase the database size.
django-mailer creates a DontSendEntry
model, which is used to filter out
recipients from messages being created.
However, note that it's actually only used when directly sending messages through
mailer.send_mail
, not when mailer is used as an alternate EMAIL_BACKEND
for Django.
Also, even if recipients become empty due to this filtering, the email will be
queued for sending anyway. (A patch to fix these issues would be accepted)