mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 03:41:05 -05:00
Add ESP templates, batch send and merge
* message.template_id to use ESP stored templates * message.merge_data and merge_global_data to supply per-recipient/global merge variables (with or without an ESP stored template) * When using per-recipient merge_data, tell ESP to use batch send: individual message per "to" address. (Mailgun does this automatically; SendGrid requires using a different "to" field; Mandrill requires `preserve_recipients=False`; Postmark doesn't support *this type* of batch sending with merge data.) * Allow message.from_email=None (must be set after init) and message.subject=None to suppress those fields in API calls (for ESPs that allow "From" and "Subject" in their template definitions). Mailgun: * Emulate merge_global_data by copying to recipient-variables for each recipient. SendGrid: * Add delimiters to merge field names via esp_extra['merge_field_format'] or ANYMAIL_SENDGRID_MERGE_FIELD_FORMAT setting. Mandrill: * Remove Djrill versions of these features; update migration notes. Closes #5.
This commit is contained in:
@@ -25,22 +25,28 @@ The table below summarizes the Anymail features supported for each ESP.
|
||||
|
||||
.. currentmodule:: anymail.message
|
||||
|
||||
=========================================== ========= ========== ========== ==========
|
||||
Email Service Provider |Mailgun| |Mandrill| |Postmark| |SendGrid|
|
||||
=========================================== ========= ========== ========== ==========
|
||||
============================================ ========== ========== ========== ==========
|
||||
Email Service Provider |Mailgun| |Mandrill| |Postmark| |SendGrid|
|
||||
============================================ ========== ========== ========== ==========
|
||||
.. rubric:: :ref:`Anymail send options <anymail-send-options>`
|
||||
-------------------------------------------------------------------------------------------
|
||||
:attr:`~AnymailMessage.metadata` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.send_at` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.tags` Yes Yes Max 1 tag Yes
|
||||
:attr:`~AnymailMessage.track_clicks` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.track_opens` Yes Yes Yes Yes
|
||||
--------------------------------------------------------------------------------------------
|
||||
:attr:`~AnymailMessage.metadata` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.send_at` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.tags` Yes Yes Max 1 tag Yes
|
||||
:attr:`~AnymailMessage.track_clicks` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.track_opens` Yes Yes Yes Yes
|
||||
|
||||
.. rubric:: :ref:`templates-and-merge`
|
||||
--------------------------------------------------------------------------------------------
|
||||
:attr:`~AnymailMessage.template_id` No Yes Yes Yes
|
||||
:attr:`~AnymailMessage.merge_data` Yes Yes No Yes
|
||||
:attr:`~AnymailMessage.merge_global_data` (emulated) Yes Yes Yes
|
||||
|
||||
.. rubric:: :ref:`Status <esp-send-status>` and :ref:`event tracking <event-tracking>`
|
||||
-------------------------------------------------------------------------------------------
|
||||
:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes
|
||||
|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes
|
||||
=========================================== ========= ========== ========== ==========
|
||||
--------------------------------------------------------------------------------------------
|
||||
:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes
|
||||
|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes
|
||||
============================================ ========== ========== ========== ==========
|
||||
|
||||
|
||||
.. .. rubric:: :ref:`inbound`
|
||||
|
||||
@@ -105,6 +105,51 @@ values directly to Mailgun. You can use any of the (non-file) parameters listed
|
||||
.. _Mailgun sending docs: https://documentation.mailgun.com/api-sending.html#sending
|
||||
|
||||
|
||||
.. _mailgun-templates:
|
||||
|
||||
Batch sending/merge and ESP templates
|
||||
-------------------------------------
|
||||
|
||||
Mailgun does not offer :ref:`ESP stored templates <esp-stored-templates>`,
|
||||
so Anymail's :attr:`~anymail.message.AnymailMessage.template_id` message
|
||||
attribute is not supported with the Mailgun backend.
|
||||
|
||||
Mailgun *does* support :ref:`batch sending <batch-send>` with per-recipient
|
||||
merge data. You can refer to Mailgun "recipient variables" in your
|
||||
message subject and body, and supply the values with Anymail's
|
||||
normalized :attr:`~anymail.message.AnymailMessage.merge_data`
|
||||
and :attr:`~anymail.message.AnymailMessage.merge_global_data`
|
||||
message attributes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
message = EmailMessage(
|
||||
...
|
||||
subject="Your order %recipient.order_no% has shipped",
|
||||
body="""Hi %recipient.name%,
|
||||
We shipped your order %recipient.order_no%
|
||||
on %recipient.ship_date%.""",
|
||||
to=["alice@example.com", "Bob <bob@example.com>"]
|
||||
)
|
||||
# (you'd probably also set a similar html body with %recipient.___% variables)
|
||||
message.merge_data = {
|
||||
'alice@example.com': {'name': "Alice", 'order_no': "12345"},
|
||||
'bob@example.com': {'name': "Bob", 'order_no': "54321"},
|
||||
}
|
||||
message.merge_global_data = {
|
||||
'ship_date': "May 15" # Anymail maps globals to all recipients
|
||||
}
|
||||
|
||||
Mailgun does not natively support global merge data. Anymail emulates
|
||||
the capability by copying any `merge_global_data` values to each
|
||||
recipient's section in Mailgun's "recipient-variables" API parameter.
|
||||
|
||||
See the `Mailgun batch sending`_ docs for more information.
|
||||
|
||||
.. _Mailgun batch sending:
|
||||
https://documentation.mailgun.com/user_manual.html#batch-sending
|
||||
|
||||
|
||||
.. _mailgun-webhooks:
|
||||
|
||||
Status tracking webhooks
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Mandrill
|
||||
========
|
||||
|
||||
Anymail integrates with the `Mandrill <http://mandrill.com/>`_
|
||||
Anymail integrates with the `Mandrill <http://mandrill.com/>`__
|
||||
transactional email service from MailChimp.
|
||||
|
||||
.. note:: **Limited Support for Mandrill**
|
||||
@@ -101,6 +101,61 @@ Anymail's Mandrill backend does not yet implement the
|
||||
:attr:`~anymail.message.AnymailMessage.esp_extra` feature.
|
||||
|
||||
|
||||
.. _mandrill-templates:
|
||||
|
||||
Batch sending/merge and ESP templates
|
||||
-------------------------------------
|
||||
|
||||
Mandrill offers both :ref:`ESP stored templates <esp-stored-templates>`
|
||||
and :ref:`batch sending <batch-send>` with per-recipient merge data.
|
||||
|
||||
You can use a Mandrill stored template by setting a message's
|
||||
:attr:`~anymail.message.AnymailMessage.template_id` to the
|
||||
template's name. Alternatively, you can refer to merge fields
|
||||
directly in an EmailMessage's subject and body---the message itself
|
||||
is used as an on-the-fly template.
|
||||
|
||||
In either case, supply the merge data values with Anymail's
|
||||
normalized :attr:`~anymail.message.AnymailMessage.merge_data`
|
||||
and :attr:`~anymail.message.AnymailMessage.merge_global_data`
|
||||
message attributes.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# This example defines the template inline, using Mandrill's
|
||||
# default MailChimp merge *|field|* syntax.
|
||||
# You could use a stored template, instead, with:
|
||||
# message.template_id = "template name"
|
||||
message = EmailMessage(
|
||||
...
|
||||
subject="Your order *|order_no|* has shipped",
|
||||
body="""Hi *|name|*,
|
||||
We shipped your order *|order_no|*
|
||||
on *|ship_date|*.""",
|
||||
to=["alice@example.com", "Bob <bob@example.com>"]
|
||||
)
|
||||
# (you'd probably also set a similar html body with merge fields)
|
||||
message.merge_data = {
|
||||
'alice@example.com': {'name': "Alice", 'order_no': "12345"},
|
||||
'bob@example.com': {'name': "Bob", 'order_no': "54321"},
|
||||
}
|
||||
message.merge_global_data = {
|
||||
'ship_date': "May 15",
|
||||
}
|
||||
|
||||
When you supply per-recipient :attr:`~anymail.message.AnymailMessage.merge_data`,
|
||||
Anymail automatically forces Mandrill's `preserve_recipients` option to false,
|
||||
so that each person in the message's "to" list sees only their own email address.
|
||||
|
||||
To use the subject or from address defined with a Mandrill template, set the message's
|
||||
`subject` or `from_email` attribute to `None`.
|
||||
|
||||
See the `Mandrill's template docs`_ for more information.
|
||||
|
||||
.. _Mandrill's template docs:
|
||||
https://mandrill.zendesk.com/hc/en-us/articles/205582507-Getting-Started-with-Templates
|
||||
|
||||
|
||||
.. _mandrill-webhooks:
|
||||
|
||||
Status tracking webhooks
|
||||
@@ -198,7 +253,8 @@ Changes to EmailMessage attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``message.send_at``
|
||||
If you are using an aware datetime for :attr:`send_at`,
|
||||
If you are using an aware datetime for
|
||||
:attr:`~anymail.message.AnymailMessage.send_at`,
|
||||
it will keep working unchanged with Anymail.
|
||||
|
||||
If you are using a date (without a time), or a naive datetime,
|
||||
@@ -211,7 +267,8 @@ Changes to EmailMessage attributes
|
||||
|
||||
``message.mandrill_response``
|
||||
Anymail normalizes ESP responses, so you don't have to be familiar
|
||||
with the format of Mandrill's JSON. See :attr:`anymail_status`.
|
||||
with the format of Mandrill's JSON.
|
||||
See :attr:`~anymail.message.AnymailMessage.anymail_status`.
|
||||
|
||||
The *raw* ESP response is attached to a sent message as
|
||||
``anymail_status.esp_response``, so the direct replacement
|
||||
@@ -221,14 +278,16 @@ Changes to EmailMessage attributes
|
||||
|
||||
mandrill_response = message.anymail_status.esp_response.json()
|
||||
|
||||
**Templates and merge variables**
|
||||
Coming to Anymail soon.
|
||||
``message.template_name``
|
||||
Anymail renames this to :attr:`~anymail.message.AnymailMessage.template_id`.
|
||||
|
||||
However, no other ESPs support MailChimp's templating language, so
|
||||
you'll need to rewrite your templates as you switch ESPs.
|
||||
``message.merge_vars`` and ``message.global_merge_vars``
|
||||
Anymail renames these to :attr:`~anymail.message.AnymailMessage.merge_data`
|
||||
and :attr:`~anymail.message.AnymailMessage.merge_global_data`, respectively.
|
||||
|
||||
Consider converting to :ref:`Django templates <django-templates>`
|
||||
instead, as these can be used with any email backend.
|
||||
``message.use_template_from`` and ``message.use_template_subject``
|
||||
With Anymail, set ``message.from_email = None`` or ``message.subject = None``
|
||||
to use the values from the stored template.
|
||||
|
||||
**Other Mandrill-specific attributes**
|
||||
Are currently still supported by Anymail's Mandrill backend,
|
||||
|
||||
@@ -115,6 +115,52 @@ see :ref:`unsupported-features`.
|
||||
Postmark does not support :attr:`~anymail.message.AnymailMessage.send_at`.
|
||||
|
||||
|
||||
.. _postmark-templates:
|
||||
|
||||
Batch sending/merge and ESP templates
|
||||
-------------------------------------
|
||||
|
||||
Postmark supports :ref:`ESP stored templates <esp-stored-templates>`
|
||||
populated with global merge data for all recipients, but does not
|
||||
offer :ref:`batch sending <batch-send>` with per-recipient merge data.
|
||||
Anymail's :attr:`~anymail.message.AnymailMessage.merge_data`
|
||||
message attribute is not supported with the Postmark backend.
|
||||
|
||||
To use a Postmark template, set the message's
|
||||
:attr:`~anymail.message.AnymailMessage.template_id` to the numeric
|
||||
Postmark "TemplateID" and supply the "TemplateModel" using
|
||||
the :attr:`~anymail.message.AnymailMessage.merge_global_data`
|
||||
message attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
message = EmailMessage(
|
||||
...
|
||||
subject=None, # use template subject
|
||||
to=["alice@example.com"] # single recipient...
|
||||
# ...multiple to emails would all get the same message
|
||||
# (and would all see each other's emails in the "to" header)
|
||||
)
|
||||
message.template_id = 80801 # use this Postmark template
|
||||
message.merge_global_data = {
|
||||
'name': "Alice",
|
||||
'order_no': "12345",
|
||||
'ship_date': "May 15",
|
||||
'items': [
|
||||
{'product': "Widget", 'price': "9.99"},
|
||||
{'product': "Gadget", 'price': "17.99"},
|
||||
],
|
||||
}
|
||||
|
||||
Set the EmailMessage's subject to `None` to use the subject from
|
||||
your Postmark template, or supply a subject with the message to override
|
||||
the template value.
|
||||
|
||||
See this `Postmark blog post on templates`_ for more information.
|
||||
|
||||
.. _Postmark blog post on templates:
|
||||
https://postmarkapp.com/blog/special-delivery-postmark-templates
|
||||
|
||||
|
||||
.. _postmark-webhooks:
|
||||
|
||||
|
||||
@@ -89,6 +89,34 @@ Default ``True``. You can set to ``False`` to disable this behavior.
|
||||
See :ref:`Message-ID quirks <sendgrid-message-id>` below.
|
||||
|
||||
|
||||
.. setting:: ANYMAIL_SENDGRID_MERGE_FIELD_FORMAT
|
||||
|
||||
.. rubric:: SENDGRID_MERGE_FIELD_FORMAT
|
||||
|
||||
If you use :ref:`merge data <merge-data>`, set this to a :meth:`str.format`
|
||||
formatting string that indicates how merge fields are delimited
|
||||
in your SendGrid templates.
|
||||
For example, if your templates use the ``-field-`` hyphen delimiters
|
||||
suggested in some SendGrid docs, you would set:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ANYMAIL = {
|
||||
...
|
||||
"SENDGRID_MERGE_FIELD_FORMAT": "-{}-",
|
||||
}
|
||||
|
||||
The placeholder `{}` will become the merge field name. If you need to include
|
||||
a literal brace character, double it up. (For example, Handlebars-style
|
||||
``{{field}}`` delimiters would take the format string `"{{{{{}}}}}"`.)
|
||||
|
||||
The default `None` requires you include the delimiters directly in your
|
||||
:attr:`~anymail.message.AnymailMessage.merge_data` keys.
|
||||
You can also override this setting for individual messages.
|
||||
See the notes on SendGrid :ref:`templates and merge <sendgrid-templates>`
|
||||
below.
|
||||
|
||||
|
||||
.. setting:: ANYMAIL_SENDGRID_API_URL
|
||||
|
||||
.. rubric:: SENDGRID_API_URL
|
||||
@@ -185,6 +213,84 @@ Limitations and quirks
|
||||
(Tested March, 2016)
|
||||
|
||||
|
||||
.. _sendgrid-templates:
|
||||
|
||||
Batch sending/merge and ESP templates
|
||||
-------------------------------------
|
||||
|
||||
SendGrid offers both :ref:`ESP stored templates <esp-stored-templates>`
|
||||
and :ref:`batch sending <batch-send>` with per-recipient merge data.
|
||||
|
||||
You can use a SendGrid stored template by setting a message's
|
||||
:attr:`~anymail.message.AnymailMessage.template_id` to the
|
||||
template's unique id. Alternatively, you can refer to merge fields
|
||||
directly in an EmailMessage's subject and body---the message itself
|
||||
is used as an on-the-fly template.
|
||||
|
||||
In either case, supply the merge data values with Anymail's
|
||||
normalized :attr:`~anymail.message.AnymailMessage.merge_data`
|
||||
and :attr:`~anymail.message.AnymailMessage.merge_global_data`
|
||||
message attributes.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
message = EmailMessage(
|
||||
...
|
||||
subject="", # don't add any additional subject content to the template
|
||||
body="", # (same thing for additional body content)
|
||||
to=["alice@example.com", "Bob <bob@example.com>"]
|
||||
)
|
||||
message.template_id = "5997fcf6-2b9f-484d-acd5-7e9a99f0dc1f" # SendGrid id
|
||||
message.merge_data = {
|
||||
'alice@example.com': {'name': "Alice", 'order_no': "12345"},
|
||||
'bob@example.com': {'name': "Bob", 'order_no': "54321"},
|
||||
}
|
||||
message.merge_global_data = {
|
||||
'ship_date': "May 15",
|
||||
}
|
||||
message.esp_extra = {
|
||||
# Tell Anymail this SendGrid template uses "-field-" to refer to merge fields.
|
||||
# (We could also just set SENDGRID_MERGE_FIELD_FORMAT in our ANYMAIL settings.)
|
||||
'merge_field_format': "-{}-"
|
||||
}
|
||||
|
||||
SendGrid doesn't have a pre-defined merge field syntax, so you
|
||||
must tell Anymail how substitution fields are delimited in your templates.
|
||||
There are three ways you can do this:
|
||||
|
||||
* Set `'merge_field_format'` in the message's
|
||||
:attr:`~anymail.message.AnymailMessage.esp_extra` to a python :meth:`str.format`
|
||||
string, as shown in the example above. (This applies only to that
|
||||
particular EmailMessage.)
|
||||
* *Or* set :setting:`SENDGRID_MERGE_FIELD_FORMAT <ANYMAIL_SENDGRID_MERGE_FIELD_FORMAT>`
|
||||
in your Anymail settings. This is usually the best approach, and will apply to all messages
|
||||
sent through SendGrid. (You can still use esp_extra to override for individual messages.)
|
||||
* *Or* include the field delimiters directly in *all* your
|
||||
:attr:`~anymail.message.AnymailMessage.merge_data` and
|
||||
:attr:`~anymail.message.AnymailMessage.merge_global_data` keys.
|
||||
E.g.: ``{'-name-': "Alice", '-order_no-': "12345"}``.
|
||||
(This can be error-prone, and difficult to move to other ESPs.)
|
||||
|
||||
When you supply per-recipient :attr:`~anymail.message.AnymailMessage.merge_data`,
|
||||
Anymail automatically changes how it communicates the "to" list to SendGrid, so that
|
||||
so that each recipient sees only their own email address. (Anymail moves the recipients
|
||||
from top-level "to" and "toname" API parameters into the "x-smtpapi" section "to" list.)
|
||||
|
||||
SendGrid templates allow you to mix your EmailMessage's `subject` and `body`
|
||||
with the template subject and body (by using `<%subject%>` and `<%body%>` in
|
||||
your SendGrid template definition where you want the message-specific versions
|
||||
to appear). If you don't want to supply any additional subject or body content
|
||||
from your Django app, set those EmailMessage attributes to empty strings.
|
||||
|
||||
See the `SendGrid's template overview`_ and `transactional template docs`_
|
||||
for more information.
|
||||
|
||||
.. _SendGrid's template overview:
|
||||
https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html
|
||||
.. _transactional template docs:
|
||||
https://sendgrid.com/docs/API_Reference/Web_API_v3/Transactional_Templates/smtpapi.html
|
||||
|
||||
|
||||
.. _sendgrid-webhooks:
|
||||
|
||||
Status tracking webhooks
|
||||
|
||||
Reference in New Issue
Block a user