Sendgrid-python: Unclear default data

Created on 23 Jun 2016  路  36Comments  路  Source: sendgrid/sendgrid-python

I find the examples difficult to run because they are unclear on what is needed (specifically https://github.com/sendgrid/sendgrid-python/blob/master/examples/mail/mail.py).

All I am trying to do is send a simple email with one of my transactional email templates:

sg = sendgrid.SendGridAPIClient(apikey='mykey')

def send_email(to, template):
    data = {
        'from': {
            'email': "[email protected]",
            'name': "Hello"
        },
        'personalizations': [{
            'to': [
                {
                    'email': "[email protected]",
                    'name': "test"
                }
            ]
        }],
        'subject': "Welcome ",
        'template_id': "my template id"
    }
    response = sg.client.mail.send.post(request_body=data)
    print(response.status_code)
    print(response.body)
    print(response.headers)

But this throws an attribute error:

AttributeError: class addinfourl has no attribute 'mro'

help wanted question

All 36 comments

Hello @tharrington,

You might want to check out our mail helper, here is an example of how to use it: https://github.com/sendgrid/sendgrid-python/blob/master/examples/helpers/mail/mail_example.py#L9

Also, the documentation may help also: https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html

Thanks!

Came here to say the same thing. I have a transactional email that I want to send with Python. The docs aren't clear on how to do that. The example you point to doesn't do that either.

@mattharrison,

Ah, I should have given this link: https://github.com/sendgrid/sendgrid-python/blob/master/examples/helpers/mail/mail_example.py#L123

Also, this is the simplest example of sending an email via this library: https://github.com/sendgrid/sendgrid-python#hello-email

Please let me know if there is further detail needed. I'm not quite sure what is missing.

Thanks!

@thinkingserious thanks for the quick response. Sorry, I should have been more clear. I have a template for a transactional email. How do I get the template, fill in the variables, and send it?

@mattharrison,

Thanks for the clarification:

  1. First setup your template here: https://sendgrid.com/templates and make sure you grab the template ID
  2. Set the template ID like this: https://github.com/sendgrid/sendgrid-python/blob/master/examples/helpers/mail/mail_example.py#L81
  3. Add substitutions like this: https://github.com/sendgrid/sendgrid-python/blob/master/examples/helpers/mail/mail_example.py#L38

Note that for substitutions, they are applied for each personalization, so you will need have a different personalization for each substitution.

Please let me know if these instructions help. If so, I'll work to provide a better explanation in the docs, if not, please help me understand what is missing.

Thanks!

@tharrington,

It looks like you are only missing the substitution inside of your personalizations block.

@thinkingserious Thanks for your help. My confusion was that I thought set_template_id would set it, rather than get it...

Will give it a try

@mattharrison,

Ah, are you saying that you thought set_template_id would be used to create a template?

@thinkingserious I don't even need substitutions... My template just says test with subject test

202
Server: nginx
Date: Fri, 01 Jul 2016 18:38:58 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Connection: close
X-Message-Id: kecdWLL7QuOpfzEXGh_MKg
X-Frame-Options: DENY

def send_email(to, subject, template, content):
    mail = Mail()
    mail.set_from(Email("[email protected]", "test"))
    mail.set_subject(subject)

    mail.add_content(Content("text/plain", content))
    mail.add_content(Content("text/html", "<html><body>" + content + "</body></html>"))

    personalization = Personalization()
    name = to.first_name + ' ' + to.last_name
    personalization.add_to(Email(to.email, name))
    personalization.set_send_at(1443636843)
    mail.add_personalization(personalization)

    mail.set_template_id("myid")
    response = sg.client.mail.send.post(request_body=mail.get())
    print(response.status_code)
    print(response.headers)
    print(response.body)

@tharrington that response code (202) means: "Your message is both valid, and queued to be delivered." (see https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html)

I never receive the message.

@tharrington do you have subject and body tags defined in your template? https://sendgrid.com/docs/User_Guide/Transactional_Templates/create_edit.html

I took them out and just put them as raw txt.

@tharrington I'm not sure what "put them as raw txt" means. You need to have a subject and body tag in your template. Please see the docs I linked to above for details.

@tharrington,

If you get a 202 response, but don't receive the email, there may be something wrong with your account. Please contact https://support.sendgrid.com so we can dig deeper.

I wrote a little helper to test out

import os

import sendgrid
from sendgrid.helpers.mail import Email, Mail, Personalization, Substitution

RESET_PW = 'Some template ID'

SG = sendgrid.SendGridAPIClient(apikey=os.environ['SENDGRID_API_KEY'])

def _send_email(template_id, subject, dst_email, dst_name,  src_email, src_name, sub_dict):
    mail = Mail()
    mail.set_subject(subject)
    mail.set_from(Email(src_email, src_name))
    p = Personalization()
    p.add_to(Email(dst_email, dst_name))
    for k, v in sub_dict.items():
        p.add_substitution(Substitution(k, v))
    mail.add_personalization(p)
    mail.set_template_id(template_id)
    data = mail.get()
    res = SG.client.mail.send.post(request_body=data)
    return res

if __name__ == '__main__':
    email = 'fill in email here'
    _send_email(RESET_PW, 'test email', email, email, email, email,{})

I have the email and RESET_PW set to valid values. I am getting a "Bad Request". I tried using curl to get more details and got the following output:

{"errors":[{"message":"Bad Request","field":null,"help":null}]}

Would love any hints on how to understand what is going on.

Hello @mattharrison,

Wrapping your _send_email function in a try/except reveals the following error:

{"errors":[{"message":"The content parameter is required. There must be at least one defined content block. We typically suggest both text/plain and text/html blocks are included, but only one block is required.","field":"content","help":"http://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html#message.content"}]}

Unfortunately, today, even when you define a template you must provide the content block. The work around is to provide an empty content block. Note that this behavior will be changing shortly, since we currently have an item in the backlog to remove the content validation when there is a template defined.

Thanks!

Lastly, I am adding in merge vars for my template:

Hi <%name%>,
Welcome!

<%body%>

Then in the code, sending in merge vars:
merge_vars = { 'name' : name }

Then iterating these merge vars and creating substitutions:

for key, value in merge_vars.iteritems() :
        personalization.add_substitution(
            Substitution("%" + key + "%", "%" + value + "%"))

This all works, but the email has the <% %> in them... for example if I have the above template with these merge vars: { "name": "Joe Smith"}

The template sends like this:
Hi <%Joe Smith%>,
Welcome!

@thanatos,

In your template, <%name%> should be %name%.

Then Substitution("%" + key + "%", "%" + value + "%") should be Substitution("%" + key + "%", value).

Hi @thinkingserious, can you elaborate on how you use a try/catch to pull out the error. Running into other issues now. Thanks!

@mattharrison,

Here you go: https://github.com/sendgrid/sendgrid-python/blob/master/TROUBLESHOOTING.md#error-messages

I hope that helps!

Not too much:

(Pdb) print(e.read())
b'{"errors":[{"message":"Bad Request","field":null,"help":null}]}'

@mattharrison,

:(

Can you provide me the contents of mail.get()? I'd like to look at the raw json you are passing to the client to see if I can catch anything.

Here's some sanitized JSON:

 "{'content': [{'value': '<html><body></body></html>', 'type': 'text/html'}], 
'to': [{'email': '[email protected]', 'name': 'some name}]}],
 'template_id': 'TEMPLATEID', 'subject': 'Subject', 
'from': {'email': '[email protected], 'name': 'person}}"

I see a few issues here:

  1. Some of the values are missing closing 's
  2. I don't see a personalizations block

Can you share the code you are using to generate this json?

The code is the code I left above (_send_email). I removed the personalizations to anonymize the data. The missing quotes are my typos sanitizing here.

@mattharrison,

Can you please send the complete code and/or the generated json to [email protected]? I will try to reproduce and figure this out for you.

Sent. Thanks!

Turned out my substitutions in Python were not strings. A simple cast (str) appeared to do the trick

def _send_email(template_id, subject, dst_email, dst_name,  src_email, src_name, sub_dict, cc_emails=None):
    mail = Mail()
    mail.set_subject(subject)
    mail.set_from(Email(src_email, src_name))
    p = Personalization()
    p.add_to(Email(dst_email, dst_name))
    for k, v in sub_dict.items():
        p.add_substitution(Substitution(k, str(v)))
    if cc_emails:
        for cc in cc_emails:
            p.add_cc(Email(cc))
    mail.add_personalization(p)
    mail.set_template_id(template_id)
    content2 = Content('text/html', '<html><body></body></html>')
    mail.add_content(content2)

    data = mail.get()
    try:
        res = SG.client.mail.send.post(request_body=data)
    except Exception as e:
        print("ERROR SENDING MAIL", e.read(), data)
        raise

Thanks for sharing the solution @mattharrison!

Thanks for your help!

The content object is no longer required when you specify a template ID.

Thank you for your patience and support!

It appears:

'Response' object has no attribute 'headers'
When
respons = sg.client.mail.send.post(request_body=data)
write_log(str(respons.headers))

Hi @fsalazarsch,

That should not be the case. What version of the library are you using?

Thanks!

Was this page helpful?
0 / 5 - 0 ratings