creating a MIME email template with images to send with python / django

前端 未结 2 1915
一个人的身影
一个人的身影 2020-12-02 21:40

In my web application I send emails occasionally using a reusable mailer application like this:

user - self.user
subject = (\"My subject\")
from = \"me@mydom         


        
相关标签:
2条回答
  • 2020-12-02 22:02

    I was having trouble with Jarret's recipe on Django 1.10 - was getting MIME and encoding errors for the various ways you can attach MIME data.

    Here's a simple multipart transactional template for an email with an embedded coupon_image file object that works on django 1.10:

    from django.core.mail import EmailMultiAlternatives
    from email.mime.image import MIMEImage
    
    def send_mail(coupon_image):
        params = {'foo':'bar'} # create a template context
        text_body = render_to_string('coupon_email.txt', params)
        html_body = render_to_string('coupon_email.html', params)
        img_data = coupon_image.read() #should be a file object, or ImageField
        img = MIMEImage(img_data)
        img.add_header('Content-ID', '<coupon_image>')
        img.add_header('Content-Disposition', 'inline', filename="coupon_image")
    
        email = EmailMultiAlternatives(
            subject="Here's your coupon!",
            body=text_body,
            from_email='noreply@example.com',
            to=['someone@example.com',]
        )
    
        email.attach_alternative(html_body, "text/html")
        email.mixed_subtype = 'related'
        email.attach(img)
    
        email.send(fail_silently=False)
    
    0 讨论(0)
  • 2020-12-02 22:07

    Update

    Many thanks to Saqib Ali for resurrecting this old question nearly 5 years after my reply.

    The instructions I gave at the time no longer work. I suspect there have been some improvements to Django in the intervening years which mean that send_mail() enforces plain text. No matter what you put in the content, it will always be delivered as plain text.

    The most recent Django documentation explains that send_mail() is really just a convenience for creating an instance of the django.core.mail.EmailMessage class, and then calling send() on that instance. EmailMessage has this note for the body parameter, which explains the results we're seeing now in 2014:

    body: The body text. This should be a plain text message.

    ... somewhat later in the docs ...

    By default, the MIME type of the body parameter in an EmailMessage is "text/plain". It is good practice to leave this alone.

    Fair enough (I confess I haven't taken the time to investigate why the 2009 instructions worked - I did test them back in 2009 - or when it changed). Django does provide, and document, a django.core.mail.EmailMultiAlternatives class to make it easier for sending a plain text and HTML representation of the same message.

    The case in this question is slightly different. We're not seeking to append an alternative per se, but to append related parts to one of the alternatives. Within the HTML version (and it doesn't matter if you have or omit the plain text version), we want to embed an image data part. Not an alternative view of the content, but related content that is referenced in the HTML body.

    Sending an embedded image is still possible, but I don't see a straightforward way to do it using send_mail. It's time to dispense with the convenience function and to instantiate an EmailMessage directly.

    Here's an update to the previous example:

    from django.core.mail import EmailMessage
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    # Load the image you want to send as bytes
    img_data = open('logo.jpg', 'rb').read()
    
    # Create a "related" message container that will hold the HTML 
    # message and the image. These are "related" (not "alternative")
    # because they are different, unique parts of the HTML message,
    # not alternative (html vs. plain text) views of the same content.
    html_part = MIMEMultipart(_subtype='related')
    
    # Create the body with HTML. Note that the image, since it is inline, is 
    # referenced with the URL cid:myimage... you should take care to make
    # "myimage" unique
    body = MIMEText('<p>Hello <img src="cid:myimage" /></p>', _subtype='html')
    html_part.attach(body)
    
    # Now create the MIME container for the image
    img = MIMEImage(img_data, 'jpeg')
    img.add_header('Content-Id', '<myimage>')  # angle brackets are important
    img.add_header("Content-Disposition", "inline", filename="myimage") # David Hess recommended this edit
    html_part.attach(img)
    
    # Configure and send an EmailMessage
    # Note we are passing None for the body (the 2nd parameter). You could pass plain text
    # to create an alternative part for this message
    msg = EmailMessage('Subject Line', None, 'foo@bar.com', ['bar@foo.com'])
    msg.attach(html_part) # Attach the raw MIMEBase descendant. This is a public method on EmailMessage
    msg.send()
    

    Original reply from 2009:

    To send an e-mail with embedded images, use python's built-in email module to build up the MIME parts.

    The following should do it:

    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    # Load the image you want to send at bytes
    img_data = open('logo.jpg', 'rb').read()
    
    # Create a "related" message container that will hold the HTML 
    # message and the image
    msg = MIMEMultipart(_subtype='related')
    
    # Create the body with HTML. Note that the image, since it is inline, is 
    # referenced with the URL cid:myimage... you should take care to make
    # "myimage" unique
    body = MIMEText('<p>Hello <img src="cid:myimage" /></p>', _subtype='html')
    msg.attach(body)
    
    # Now create the MIME container for the image
    img = MIMEImage(img_data, 'jpeg')
    img.add_header('Content-Id', '<myimage>')  # angle brackets are important
    msg.attach(img)
    
    send_mail(subject, msg.as_string(), from, [to], priority="high")
    

    In reality, you'll probably want to send the HTML along with a plain-text alternative. In that case, use MIMEMultipart to create the "related" mimetype container as the root. Then create another MIMEMultipart with the subtype "alternative", and attach both a MIMEText (subtype html) and a MIMEText (subtype plain) to the alternative part. Then attach the image to the related root.

    0 讨论(0)
提交回复
热议问题