Open ghost opened 3 years ago
This issue has been added to our internal backlog to be prioritized. Pull requests and +1s on the issue summary will help it move up the backlog.
+1 on this -- the workaround I have implemented as a stopgap is looping through all emails to send to a single address at a time. Not great for latency, but works for now.
Just an FYI to anyone considering picking this up: The C# library already has this feature implemented as SendGrid.Helpers.Mail.MailHelper.CreateSingleTemplateEmailToMultipleRecipients
. Effectively, it just internalizes what @ksho mentioned above -- if showAllRecipients is false (default), it creates a personalization for each recipient and copies the template data into it. This pattern might be useful for implementation in Python.
..and just to clarify a bit further, that workaround is something like:
members = list of email strings
for email in members:
message = Mail(
from_email='foo@bar.com',
to_emails=email,
subject='lalala')
message.template_id = TemplateId('d-123456')
request_body = message.get()
request_body['personalizations'][0]['dynamic_template_data'] = substitution_data
try:
r = sg.client.mail.send.post(request_body=request_body)
I have a pretty good idea of how this could be implemented...
Here's the way the code should work without a builtin helper from sendgrid.helpers.mail
:
import sendgrid
from sendgrid.helpers.mail import Mail
if __name__ == "__main__":
# We don't want these emails to be able to see each other in the "To" bar, but they should all receive the same email.
tos = [
{'name': 'example', 'email': 'example@test.com'}
{'name': 'example1', 'email': 'example1@test.com'}
{'name': 'example2', 'email': 'example2@test.com'}
]
dynamic_template_data = {
'subject': 'This will be individually copied to each message.'
}
message = Mail(from_email='myemail@me.com')
message.template_id = "something"
# This is how we'd do it in C#, and this pattern is embedded into helper methods on MailHelper
# the MailHelper class' helper methods in C# could easily be modelled as class methods on the Mail object in Python
for i, to in enumerate(tos):
message.add_to(to['email'], p=i)
# this is what needs to be implemented and then incorporated into a constructor/class method on Mail
# to be abundently clear, Mail.set_template_data does not currently exist
message.set_template_data(dynamic_template_data, p=i)
client = sendgrid.SendGridAPIClient(key)
client.send(message)
And now with a class method helper:
import sendgrid
from sendgrid.helpers.mail import Mail
if __name__ == "__main__":
# We don't want these emails to be able to see each other in the "To" bar, but they should all receive the same email.
tos = [
{'name': 'example', 'email': 'example@test.com'}
{'name': 'example1', 'email': 'example1@test.com'}
{'name': 'example2', 'email': 'example2@test.com'}
]
dynamic_template_data = {
'subject': 'This will be individually copied to each message.'
}
message = Mail.create_single_template_email_with_multiple_recipients(
from_email='myemail@me.com',
tos=tos, # I don't remember if the object format above is appropriate for the Python library, but this at least proves the pattern
plain_text_content="hi!",
html_content="<p>hi!</p>"
dynamic_template_data=dynamic_template_data)
client = sendgrid.SendGridAPIClient(key)
client.send(message)
To achieve this, we'd need five class methods on Mail:
# Pretend there's a subject/plaintext/html argument for each of the following -- I don't want to write them out
@classmethod
def create_single_email(cls, from_email: str, to_email: str, template_id: str, dynamic_template_data: object) {
#return an instance of the class with the required personalization
}
@classmethod
def create_single_email_to_multiple_recipients(cls, from_email: str, to_emails: list<str>) {
#return an instance of the class with the required personalizations (one for each to)
#super easy -- instantiate the class, then just iterate over enumerate(to_emails), calling
#classobject.add_to(email, i)
}
@classmethod
def create_single_template_email_to_multiple_recipients(yougettheidea) {
#Same as the above, but call classobject.add_to(email, i) and then classobject.set_template_data(dynamic_template_data, i) (not implemented)
}
#Two more class methods with similar logic for create_multiple_emails_to_multiple_recipients and create_multiple_template_emails_to_multiple_recipients
I think the only other methods we'd have to implement are (instance methods on Mail):
def set_template_data(self, dynamic_template_data: object, p: int):
personalization = self.GetPersonalization(p)
personalization.TemplateData = dynamic_template_data
def get_personalization(self, p: int):
# return self's personalization at the given index, assuming it exists. Throw index errors if we're being messy.
# if there's no personalization, create one at index 0 and return it
I'm sure there are some implementation details that might make this slightly more difficult, but those are the bones we'd need. If I wasn't so busy, I'd try to tackle it myself.
+1 on this—eating up API requests trying to workaround
Works in sendgrid==6.6.0
and python 3.7.9 as documentated here:
https://github.com/sendgrid/sendgrid-python/blob/main/use_cases/send_multiple_emails_to_multiple_recipients.md
dynamic_template_data
needs to be passed in To()
constructor.
Issue Summary
Hey all,
Using the mail helper with
is_multiple=True
causes thedynamic_template_data
on the message not to propagate to the actual message. It seems that this data has to somehow be included individually in eachpersonalization
object, but I don't think I can set theMail
object'spersonalizations
property manually, meaning there's no way to push thedynamic_template_data
down into each of those objects.If this is a bug, I would suggest automatically copying the
dynamic_template_data
to each personalization object whenis_multiple
is set toTrue
.Steps to Reproduce
Mail(from_email='someone@something.com', to_emails=['one', 'two', ...], is_multiple=True
dynamic_template_data
property.template_id
property.Code Snippet
Exception/Log
N/A, code executes "successfully".
Technical details: