mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51: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:
@@ -56,6 +56,12 @@ class MailgunPayload(RequestsPayload):
|
||||
auth = ("api", backend.api_key)
|
||||
self.sender_domain = None
|
||||
self.all_recipients = [] # used for backend.parse_recipient_status
|
||||
|
||||
# late-binding of recipient-variables:
|
||||
self.merge_data = None
|
||||
self.merge_global_data = None
|
||||
self.to_emails = []
|
||||
|
||||
super(MailgunPayload, self).__init__(message, defaults, backend, auth=auth, *args, **kwargs)
|
||||
|
||||
def get_api_endpoint(self):
|
||||
@@ -66,6 +72,34 @@ class MailgunPayload(RequestsPayload):
|
||||
backend=self.backend, email_message=self.message, payload=self)
|
||||
return "%s/messages" % self.sender_domain
|
||||
|
||||
def serialize_data(self):
|
||||
self.populate_recipient_variables()
|
||||
return self.data
|
||||
|
||||
def populate_recipient_variables(self):
|
||||
"""Populate Mailgun recipient-variables header from merge data"""
|
||||
merge_data = self.merge_data
|
||||
|
||||
if self.merge_global_data is not None:
|
||||
# Mailgun doesn't support global variables.
|
||||
# We emulate them by populating recipient-variables for all recipients.
|
||||
if merge_data is not None:
|
||||
merge_data = merge_data.copy() # don't modify the original, which doesn't belong to us
|
||||
else:
|
||||
merge_data = {}
|
||||
for email in self.to_emails:
|
||||
try:
|
||||
recipient_data = merge_data[email]
|
||||
except KeyError:
|
||||
merge_data[email] = self.merge_global_data
|
||||
else:
|
||||
# Merge globals (recipient_data wins in conflict)
|
||||
merge_data[email] = self.merge_global_data.copy()
|
||||
merge_data[email].update(recipient_data)
|
||||
|
||||
if merge_data is not None:
|
||||
self.data['recipient-variables'] = self.serialize_json(merge_data)
|
||||
|
||||
#
|
||||
# Payload construction
|
||||
#
|
||||
@@ -87,8 +121,10 @@ class MailgunPayload(RequestsPayload):
|
||||
def set_recipients(self, recipient_type, emails):
|
||||
assert recipient_type in ["to", "cc", "bcc"]
|
||||
if emails:
|
||||
self.data[recipient_type] = [str(email) for email in emails]
|
||||
self.data[recipient_type] = [email.address for email in emails]
|
||||
self.all_recipients += emails # used for backend.parse_recipient_status
|
||||
if recipient_type == 'to':
|
||||
self.to_emails = [email.email for email in emails] # used for populate_recipient_variables
|
||||
|
||||
def set_subject(self, subject):
|
||||
self.data["subject"] = subject
|
||||
@@ -145,6 +181,17 @@ class MailgunPayload(RequestsPayload):
|
||||
def set_track_opens(self, track_opens):
|
||||
self.data["o:tracking-opens"] = "yes" if track_opens else "no"
|
||||
|
||||
# template_id: Mailgun doesn't offer stored templates.
|
||||
# (The message body and other fields *are* the template content.)
|
||||
|
||||
def set_merge_data(self, merge_data):
|
||||
# Processed at serialization time (to allow merging global data)
|
||||
self.merge_data = merge_data
|
||||
|
||||
def set_merge_global_data(self, merge_global_data):
|
||||
# Processed at serialization time (to allow merging global data)
|
||||
self.merge_global_data = merge_global_data
|
||||
|
||||
def set_esp_extra(self, extra):
|
||||
self.data.update(extra)
|
||||
# Allow override of sender_domain via esp_extra
|
||||
|
||||
Reference in New Issue
Block a user