|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- ==================
- Django Post Office
- ==================
-
- Django Post Office is a simple app to send and manage your emails in Django.
- Some awesome features are:
-
- * Allows you to send email asynchronously
- * Multi backend support
- * Supports HTML email
- * Supports database based email templates
- * Built in scheduling support
- * Works well with task queues like `RQ <http://python-rq.org>`_ or `Celery <http://www.celeryproject.org>`_
- * Uses multiprocessing (and threading) to send a large number of emails in parallel
- * Supports multilingual email templates (i18n)
-
-
- Dependencies
- ============
-
- * `django >= 1.8 <http://djangoproject.com/>`_
- * `django-jsonfield <https://github.com/bradjasper/django-jsonfield>`_
-
-
- Installation
- ============
-
- |Build Status|
-
-
- * Install from PyPI (or you `manually download from PyPI <http://pypi.python.org/pypi/django-post_office>`_)::
-
- pip install django-post_office
-
- * Add ``post_office`` to your INSTALLED_APPS in django's ``settings.py``:
-
- .. code-block:: python
-
- INSTALLED_APPS = (
- # other apps
- "post_office",
- )
-
- * Run ``migrate``::
-
- python manage.py migrate
-
- * Set ``post_office.EmailBackend`` as your ``EMAIL_BACKEND`` in django's ``settings.py``:
-
- .. code-block:: python
-
- EMAIL_BACKEND = 'post_office.EmailBackend'
-
-
- Quickstart
- ==========
-
- Send a simple email is really easy:
-
- .. code-block:: python
-
- from post_office import mail
-
- mail.send(
- 'recipient@example.com', # List of email addresses also accepted
- 'from@example.com',
- subject='My email',
- message='Hi there!',
- html_message='Hi <strong>there</strong>!',
- )
-
-
- If you want to use templates, ensure that Django's admin interface is enabled. Create an
- ``EmailTemplate`` instance via ``admin`` and do the following:
-
- .. code-block:: python
-
- from post_office import mail
-
- mail.send(
- 'recipient@example.com', # List of email addresses also accepted
- 'from@example.com',
- template='welcome_email', # Could be an EmailTemplate instance or name
- context={'foo': 'bar'},
- )
-
- The above command will put your email on the queue so you can use the
- command in your webapp without slowing down the request/response cycle too much.
- To actually send them out, run ``python manage.py send_queued_mail``.
- You can schedule this management command to run regularly via cron::
-
- * * * * * (/usr/bin/python manage.py send_queued_mail >> send_mail.log 2>&1)
-
- or, if you use uWSGI_ as application server, add this short snipped to the
- project's ``wsgi.py`` file:
-
- .. code-block:: python
-
- from django.core.wsgi import get_wsgi_application
-
- application = get_wsgi_application()
-
- # add this block of code
- try:
- import uwsgidecorators
- from django.core.management import call_command
-
- @uwsgidecorators.timer(10)
- def send_queued_mail(num):
- """Send queued mail every 10 seconds"""
- call_command('send_queued_mail', processes=1)
-
- except ImportError:
- print("uwsgidecorators not found. Cron and timers are disabled")
-
- Alternatively you can also use the decorator ``@uwsgidecorators.cron(minute, hour, day, month, weekday)``.
- This will schedule a task at specific times. Use ``-1`` to signal any time, it corresponds to the ``*``
- in cron.
-
- Please note that ``uwsgidecorators`` are available only, if the application has been started
- with **uWSGI**. However, Django's internal ``./manange.py runserver`` also access this file,
- therefore wrap the block into an exception handler as shown above.
-
- This configuration is very useful in environments, such as Docker containers, where you
- don't have a running cron-daemon.
-
-
- Usage
- =====
-
- mail.send()
- -----------
-
- ``mail.send`` is the most important function in this library, it takes these
- arguments:
-
- +--------------------+----------+--------------------------------------------------+
- | Argument | Required | Description |
- +--------------------+----------+--------------------------------------------------+
- | recipients | Yes | list of recipient email addresses |
- +--------------------+----------+--------------------------------------------------+
- | sender | No | Defaults to ``settings.DEFAULT_FROM_EMAIL``, |
- | | | display name is allowed (``John <john@a.com>``) |
- +--------------------+----------+--------------------------------------------------+
- | subject | No | Email subject (if ``template`` is not specified) |
- +--------------------+----------+--------------------------------------------------+
- | message | No | Email content (if ``template`` is not specified) |
- +--------------------+----------+--------------------------------------------------+
- | html_message | No | HTML content (if ``template`` is not specified) |
- +--------------------+----------+--------------------------------------------------+
- | template | No | ``EmailTemplate`` instance or name |
- +--------------------+----------+--------------------------------------------------+
- | language | No | Language in which you want to send the email in |
- | | | (if you have multilingual email templates.) |
- +--------------------+----------+--------------------------------------------------+
- | cc | No | list emails, will appear in ``cc`` field |
- +--------------------+----------+--------------------------------------------------+
- | bcc | No | list of emails, will appear in `bcc` field |
- +--------------------+----------+--------------------------------------------------+
- | attachments | No | Email attachments - A dictionary where the keys |
- | | | are the filenames and the values are either: |
- | | | |
- | | | * files |
- | | | * file-like objects |
- | | | * full path of the file |
- +--------------------+----------+--------------------------------------------------+
- | context | No | A dictionary, used to render templated email |
- +--------------------+----------+--------------------------------------------------+
- | headers | No | A dictionary of extra headers on the message |
- +--------------------+----------+--------------------------------------------------+
- | scheduled_time | No | A date/datetime object indicating when the email |
- | | | should be sent |
- +--------------------+----------+--------------------------------------------------+
- | priority | No | ``high``, ``medium``, ``low`` or ``now`` |
- | | | (send_immediately) |
- +--------------------+----------+--------------------------------------------------+
- | backend | No | Alias of the backend you want to use. |
- | | | ``default`` will be used if not specified. |
- +--------------------+----------+--------------------------------------------------+
- | render_on_delivery | No | Setting this to ``True`` causes email to be |
- | | | lazily rendered during delivery. ``template`` |
- | | | is required when ``render_on_delivery`` is True. |
- | | | This way content is never stored in the DB. |
- | | | May result in significant space savings. |
- +--------------------+----------+--------------------------------------------------+
-
-
- Here are a few examples.
-
- If you just want to send out emails without using database templates. You can
- call the ``send`` command without the ``template`` argument.
-
- .. code-block:: python
-
- from post_office import mail
-
- mail.send(
- ['recipient1@example.com'],
- 'from@example.com',
- subject='Welcome!',
- message='Welcome home, {{ name }}!',
- html_message='Welcome home, <b>{{ name }}</b>!',
- headers={'Reply-to': 'reply@example.com'},
- scheduled_time=date(2014, 1, 1),
- context={'name': 'Alice'},
- )
-
- ``post_office`` is also task queue friendly. Passing ``now`` as priority into
- ``send_mail`` will deliver the email right away (instead of queuing it),
- regardless of how many emails you have in your queue:
-
- .. code-block:: python
-
- from post_office import mail
-
- mail.send(
- ['recipient1@example.com'],
- 'from@example.com',
- template='welcome_email',
- context={'foo': 'bar'},
- priority='now',
- )
-
- This is useful if you already use something like `django-rq <https://github.com/ui/django-rq>`_
- to send emails asynchronously and only need to store email related activities and logs.
-
- If you want to send an email with attachments:
-
- .. code-block:: python
-
- from django.core.files.base import ContentFile
- from post_office import mail
-
- mail.send(
- ['recipient1@example.com'],
- 'from@example.com',
- template='welcome_email',
- context={'foo': 'bar'},
- priority='now',
- attachments={
- 'attachment1.doc': '/path/to/file/file1.doc',
- 'attachment2.txt': ContentFile('file content'),
- 'attachment3.txt': { 'file': ContentFile('file content'), 'mimetype': 'text/plain'},
- }
- )
-
- Template Tags and Variables
- ---------------------------
-
- ``post-office`` supports Django's template tags and variables.
- For example, if you put "Hello, {{ name }}" in the subject line and pass in
- ``{'name': 'Alice'}`` as context, you will get "Hello, Alice" as subject:
-
- .. code-block:: python
-
- from post_office.models import EmailTemplate
- from post_office import mail
-
- EmailTemplate.objects.create(
- name='morning_greeting',
- subject='Morning, {{ name|capfirst }}',
- content='Hi {{ name }}, how are you feeling today?',
- html_content='Hi <strong>{{ name }}</strong>, how are you feeling today?',
- )
-
- mail.send(
- ['recipient@example.com'],
- 'from@example.com',
- template='morning_greeting',
- context={'name': 'alice'},
- )
-
- # This will create an email with the following content:
- subject = 'Morning, Alice',
- content = 'Hi alice, how are you feeling today?'
- content = 'Hi <strong>alice</strong>, how are you feeling today?'
-
-
- Multilingual Email Templates
- ----------------------------
-
- You can easily create email templates in various different languanges.
- For example:
-
- .. code-block:: python
-
- template = EmailTemplate.objects.create(
- name='hello',
- subject='Hello world!',
- )
-
- # Add an Indonesian version of this template:
- indonesian_template = template.translated_templates.create(
- language='id',
- subject='Halo Dunia!'
- )
-
- Sending an email using template in a non default languange is
- also similarly easy:
-
- .. code-block:: python
-
- mail.send(
- ['recipient@example.com'],
- 'from@example.com',
- template=template, # Sends using the default template
- )
-
- mail.send(
- ['recipient@example.com'],
- 'from@example.com',
- template=template,
- language='id', # Sends using Indonesian template
- )
-
- Custom Email Backends
- ---------------------
-
- By default, ``post_office`` uses django's ``smtp.EmailBackend``. If you want to
- use a different backend, you can do so by configuring ``BACKENDS``.
-
- For example if you want to use `django-ses <https://github.com/hmarr/django-ses>`_::
-
- POST_OFFICE = {
- 'BACKENDS': {
- 'default': 'smtp.EmailBackend',
- 'ses': 'django_ses.SESBackend',
- }
- }
-
- You can then choose what backend you want to use when sending mail:
-
- .. code-block:: python
-
- # If you omit `backend_alias` argument, `default` will be used
- mail.send(
- ['recipient@example.com'],
- 'from@example.com',
- subject='Hello',
- )
-
- # If you want to send using `ses` backend
- mail.send(
- ['recipient@example.com'],
- 'from@example.com',
- subject='Hello',
- backend='ses',
- )
-
-
- Management Commands
- -------------------
-
- * ``send_queued_mail`` - send queued emails, those aren't successfully sent
- will be marked as ``failed``. Accepts the following arguments:
-
- +---------------------------+--------------------------------------------------+
- | Argument | Description |
- +---------------------------+--------------------------------------------------+
- | ``--processes`` or ``-p`` | Number of parallel processes to send email. |
- | | Defaults to 1 |
- +---------------------------+--------------------------------------------------+
- | ``--lockfile`` or ``-L`` | Full path to file used as lock file. Defaults to |
- | | ``/tmp/post_office.lock`` |
- +---------------------------+--------------------------------------------------+
-
-
- * ``cleanup_mail`` - delete all emails created before an X number of days
- (defaults to 90).
-
- +---------------------------+--------------------------------------------------+
- | Argument | Description |
- +---------------------------+--------------------------------------------------+
- | ``--days`` or ``-d`` | Email older than this argument will be deleted. |
- | | Defaults to 90 |
- +---------------------------+--------------------------------------------------+
- | ``--delete-attachments`` | Flag to delete orphaned attachment records and |
- | or ``-da`` | files on disk. If flag is not set, |
- | | on disk attachments files won't be deleted. |
- +---------------------------+--------------------------------------------------+
-
-
- You may want to set these up via cron to run regularly::
-
- * * * * * (cd $PROJECT; python manage.py send_queued_mail --processes=1 >> $PROJECT/cron_mail.log 2>&1)
- 0 1 * * * (cd $PROJECT; python manage.py cleanup_mail --days=30 --delete-attachments >> $PROJECT/cron_mail_cleanup.log 2>&1)
-
- Settings
- ========
- This section outlines all the settings and configurations that you can put
- in Django's ``settings.py`` to fine tune ``post-office``'s behavior.
-
- Batch Size
- ----------
-
- If you may want to limit the number of emails sent in a batch (sometimes useful
- in a low memory environment), use the ``BATCH_SIZE`` argument to limit the
- number of queued emails fetched in one batch.
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'BATCH_SIZE': 50
- }
-
- Default Priority
- ----------------
-
- The default priority for emails is ``medium``, but this can be altered by
- setting ``DEFAULT_PRIORITY``. Integration with asynchronous email backends
- (e.g. based on Celery) becomes trivial when set to ``now``.
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'DEFAULT_PRIORITY': 'now'
- }
-
- Log Level
- ---------
-
- The default log level is 2 (logs both successful and failed deliveries)
- This behavior can be changed by setting ``LOG_LEVEL``.
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'LOG_LEVEL': 1 # Log only failed deliveries
- }
-
- The different options are:
-
- * ``0`` logs nothing
- * ``1`` logs only failed deliveries
- * ``2`` logs everything (both successful and failed delivery attempts)
-
-
- Sending Order
- -------------
-
- The default sending order for emails is ``-priority``, but this can be altered by
- setting ``SENDING_ORDER``. For example, if you want to send queued emails in FIFO order :
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'SENDING_ORDER': ['created']
- }
-
- Context Field Serializer
- ------------------------
-
- If you need to store complex Python objects for deferred rendering
- (i.e. setting ``render_on_delivery=True``), you can specify your own context
- field class to store context variables. For example if you want to use
- `django-picklefield <https://github.com/gintas/django-picklefield/tree/master/src/picklefield>`_:
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'CONTEXT_FIELD_CLASS': 'picklefield.fields.PickledObjectField'
- }
-
- ``CONTEXT_FIELD_CLASS`` defaults to ``jsonfield.JSONField``.
-
- Logging
- -------
-
- You can configure ``post-office``'s logging from Django's ``settings.py``. For
- example:
-
- .. code-block:: python
-
- LOGGING = {
- "version": 1,
- "disable_existing_loggers": False,
- "formatters": {
- "post_office": {
- "format": "[%(levelname)s]%(asctime)s PID %(process)d: %(message)s",
- "datefmt": "%d-%m-%Y %H:%M:%S",
- },
- },
- "handlers": {
- "post_office": {
- "level": "DEBUG",
- "class": "logging.StreamHandler",
- "formatter": "post_office"
- },
- # If you use sentry for logging
- 'sentry': {
- 'level': 'ERROR',
- 'class': 'raven.contrib.django.handlers.SentryHandler',
- },
- },
- 'loggers': {
- "post_office": {
- "handlers": ["post_office", "sentry"],
- "level": "INFO"
- },
- },
- }
-
-
- Threads
- -------
-
- ``post-office`` >= 3.0 allows you to use multiple threads to dramatically speed up
- the speed at which emails are sent. By default, ``post-office`` uses 5 threads per process.
- You can tweak this setting by changing ``THREADS_PER_PROCESS`` setting.
-
- This may dramatically increase the speed of bulk email delivery, depending on which email
- backends you use. In my tests, multi threading speeds up email backends that use HTTP based
- (REST) delivery mechanisms but doesn't seem to help SMTP based backends.
-
- .. code-block:: python
-
- # Put this in settings.py
- POST_OFFICE = {
- 'THREADS_PER_PROCESS': 10
- }
-
-
- Performance
- ===========
-
- Caching
- -------
-
- if Django's caching mechanism is configured, ``post_office`` will cache
- ``EmailTemplate`` instances . If for some reason you want to disable caching,
- set ``POST_OFFICE_CACHE`` to ``False`` in ``settings.py``:
-
- .. code-block:: python
-
- ## All cache key will be prefixed by post_office:template:
- ## To turn OFF caching, you need to explicitly set POST_OFFICE_CACHE to False in settings
- POST_OFFICE_CACHE = False
-
- ## Optional: to use a non default cache backend, add a "post_office" entry in CACHES
- CACHES = {
- 'post_office': {
- 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
- 'LOCATION': '127.0.0.1:11211',
- }
- }
-
-
- send_many()
- -----------
-
- ``send_many()`` is much more performant (generates less database queries) when
- sending a large number of emails. ``send_many()`` is almost identical to ``mail.send()``,
- with the exception that it accepts a list of keyword arguments that you'd
- usually pass into ``mail.send()``:
-
- .. code-block:: python
-
- from post_office import mail
-
- first_email = {
- 'sender': 'from@example.com',
- 'recipients': ['alice@example.com'],
- 'subject': 'Hi!',
- 'message': 'Hi Alice!'
- }
- second_email = {
- 'sender': 'from@example.com',
- 'recipients': ['bob@example.com'],
- 'subject': 'Hi!',
- 'message': 'Hi Bob!'
- }
- kwargs_list = [first_email, second_email]
-
- mail.send_many(kwargs_list)
-
- Attachments are not supported with ``mail.send_many()``.
-
-
- Running Tests
- =============
-
- To run the test suite::
-
- `which django-admin.py` test post_office --settings=post_office.test_settings --pythonpath=.
-
- You can run the full test suite with::
-
- tox
-
- or::
-
- python setup.py test
-
-
- Changelog
- =========
-
- Version 3.1.0 (2018-07-24)
- --------------------------
- * Improvements to attachments are handled. Thanks @SeiryuZ!
- * Added ``--delete-attachments`` flag to ``cleanup_mail`` management command. Thanks @Seiryuz!
- * I18n improvements. Thanks @vsevolod-skripnik and @delneg!
- * Django admin improvements. Thanks @kakulukia!
-
-
- Version 3.0.4
- -------------
- * Added compatibility with Django 2.0. Thanks @PreActionTech and @PetrDlouhy!
- * Added natural key support to `EmailTemplate` model. Thanks @maximlomakin!
-
-
- Version 3.0.2
- -------------
- - Fixed memory leak when multiprocessing is used.
- - Fixed a possible error when adding a new email from Django admin. Thanks @ivlevdenis!
-
-
- Version 3.0.2
- -------------
- - `_send_bulk` now properly catches exceptions when preparing email messages.
-
-
- Version 3.0.1
- -------------
- - Fixed an infinite loop bug in `send_queued_mail` management command.
-
-
- Version 3.0.0
- -------------
- * `_send_bulk` now allows each process to use multiple threads to send emails.
- * Added support for mimetypes in email attachments. Thanks @clickonchris!
- * An `EmailTemplate` can now be used as defaults multiple times in one language. Thanks @sac7e!
- * `send_queued_mail` management command will now check whether there are more queued emails to be sent before exiting.
- * Drop support for Django < 1.8. Thanks @fendyh!
-
-
- Full changelog can be found `here <https://github.com/ui/django-post_office/blob/master/CHANGELOG.md>`_.
-
-
- Created and maintained by the cool guys at `Stamps <https://stamps.co.id>`_,
- Indonesia's most elegant CRM/loyalty platform.
-
-
- .. |Build Status| image:: https://travis-ci.org/ui/django-post_office.png?branch=master
- :target: https://travis-ci.org/ui/django-post_office
-
- .. _uWSGI: https://uwsgi-docs.readthedocs.org/en/latest/
-
-
|