Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

Sending HTML emails with embedded images from Django

$
0
0

Currently I'm working on an application which sends HTML emails with embedded or inline images and multiple CSV and PDF attachments.

Let's assume that we will be using an object containing data for our email. I'm providing my models here just as a reference to get the idea:

class RenderedReport(models.Model): report = models.ForeignKey('Report', related_name='rendered_reports') approved_by = models.ForeignKey('auth.User', blank=True, null=True) date_rendered = models.DateTimeField(auto_now_add=True) date_queried = models.DateTimeField(blank=True, null=True) date_approved = models.DateTimeField(blank=True, null=True) date_sent = models.DateTimeField(blank=True, null=True) class Meta: get_latest_by = 'date_rendered' ordering = ('-date_rendered',) def __unicode__(self): return str(self.report) class RenderedView(models.Model): rendered_report = models.ForeignKey('RenderedReport', related_name='rendered_views') view = models.ForeignKey('View', related_name='rendered_views') png = ImageField(upload_to='reports') pdf = models.FileField(upload_to='reports', blank=True) csv = models.FileField(upload_to='reports', blank=True) class Meta: ordering = ['view'] def __unicode__(self): return str(self.view) @property def image_filename(self): return os.path.basename(self.png.name)

I don't like the idea of re-inventing the wheel, so I will be using a responsive email template from Zurb Studios .

I'm skipping the entire HTML template code for brevity because it wasn't modified. We only need this part:

<!-- BODY --> <table class="body-wrap"> <tr> <td></td> <td class="container" bgcolor="#FFFFFF"> <div class="content"> <table> <tr> <td> {% for view in views %} <h3>{{ view }}</h3> <p><img src="cid:{{ view.image_filename }}" /></p> {% if not forloop.last %}<p> </p>{% endif %} {% endfor %} </td> </tr> </table> </div> </td> <td></td> </tr> </table><!-- /BODY -->

Now it's time to get our HTML email rendered. We won't be sending a plain-text version of our email,

I'm providing a simplified, but a working snippet of python code:

import os from email.mime.image import MIMEImage from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string rendered_report = RenderedReport.objects.get(pk=1) views = rendered_report.rendered_views.all() context = {'views': views} html_content = render_to_string('reports/email.html', context=context).strip() subject = 'HTML Email' recipients = ['john.doe@test.com'] reply_to = ['noreply@test.com'] msg = EmailMultiAlternatives(subject, html_content, config.formatted_email_from, to, reply_to=reply_to) msg.content_subtype = 'html' # Main content is text/html msg.mixed_subtype = 'related' # This is critical, otherwise images will be displayed as attachments! for view in views: # Create an inline attachment image = MIMEImage(view.png.read()) image.add_header('Content-ID', '<{}>'.format(view.image_filename)) msg.attach(image) # Create a regular attachment with a CSV file if view.csv: filename = os.path.basename(view.csv.name) msg.attach(filename, view.csv.read(), 'text/csv') # Create a regular attachment with a PDF file if view.pdf: filename = os.path.basename(view.pdf.name) msg.attach(filename, view.pdf.read(), 'application/pdf') msg.send()

This will send a responsive HTML email containing inline images and attachments.

Please pay additional attention to the line with msg.mixed_subtype = 'related' . It sets the email header Content-Type: multipart/related; guaranteeing that your images will be displayed inline and not as attachments.

I'm providing an example how the <img> tag will be rendered: <img src="cid:20161010_dailykpisnapshot_OCuZ4O4.png">

And here's the email headers:

Content-Type: image/png Content-Disposition: inline Content-Transfer-Encoding: base64 Content-ID: <20161010_dailykpisnapshot_OCuZ4O4.png>


Viewing all articles
Browse latest Browse all 9596

Trending Articles