views:

683

answers:

1

I'm using django.core.mail.EmailMultiAlternatives when sending e-mails from my django app in an attempt to make sure that the message downgrades to text if the e-mail client doesn't support HTML.

Here is my send_email method:

def send_email(self, from_address, to_list, subject, msg_text, msg_html):
        subject=subject.replace('\r','').replace('\n',' ')
        self.msg = EmailMultiAlternatives(subject, msg_text, from_address, to_list)
        self.msg.attach_alternative(msg_html, "text/html")
        self.msg.content_subtype = "html"
        self.msg.send()

It works great with Gmail, Hotmail and many other e-mail clients - displaying the HTML content without a problem. But it will not display the HTML content in Outlook 2003 running on Win2003 - just the text version.

If I forcefully put the HTML in the EmailMultiAlternatives call, i.e. use msg_html instead of msg_text like so:

self.msg = EmailMultiAlternatives(subject, msg_html, from_address, to_list)

then it works correctly in all clients; but that means that there is no text fallback for clients that don't support HTML or (more likely) that have disabled support for it.

I think it is worth mentioning that the e-mail is being generated on a django app running on Mac OS X (just in case it has to do with line terminator differences between the OSes).

I see that people using other languages have had similar problems with outlook...

I wonder if anyone has any idea of WHY outlook would behave differently and if there is simple fix that can be applied in my code?

+5  A: 

I don't have an Outlook installation available to test this, so I'm wondering about the reason for the fifth line in your function.

self.msg.content_subtype = "html"

I don't know much about multipart email internals, but on my system that line causes both parts of the message have a content-type of text/html. Leaving it out produces a message with "Content-Type: text/plain" on the first part and "Content-Type: text/html" on the second.

In any case, one of the answers to the question about Java mentions changing the character set to iso-8859-1. I think you should be able to do that with django.core.mail.

The EmailMessage class (from which EmailMultiAlternatives inherits) has an attribute named "encoding" which sets the charset to use. By default it's None so the default charset of utf-8 (unless overridden in settings) is used instead.

In other words, add something like the following before the send line in the function listed in the question:

self.msg.content_subtype = "iso-8859-1"

Unfortunately, that will only change the encoding specified on the first part (msg_text in the function above). The function that attaches the alternative content doesn't seem to use the encoding attribute. I'm not sure it's the correct approach but I subclassed EmailMultiAlternatives to override the relevant function and it seemed to work okay.

class EmailMultiAlternativesWithEncoding(EmailMultiAlternatives):
    def _create_attachment(self, filename, content, mimetype=None):
        """
        Converts the filename, content, mimetype triple into a MIME attachment
        object. Use self.encoding when handling text attachments.
        """
        if mimetype is None:
            mimetype, _ = mimetypes.guess_type(filename)
            if mimetype is None:
                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
        basetype, subtype = mimetype.split('/', 1)
        if basetype == 'text':
            encoding = self.encoding or settings.DEFAULT_CHARSET
            attachment = SafeMIMEText(smart_str(content,
                settings.DEFAULT_CHARSET), subtype, encoding)
            # original text being replaced above (not last argument)
            # attachment = SafeMIMEText(smart_str(content,
            #     settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
        else:
            # Encode non-text attachments with base64.
            attachment = MIMEBase(basetype, subtype)
            attachment.set_payload(content)
            Encoders.encode_base64(attachment)
        if filename:
            attachment.add_header('Content-Disposition', 'attachment',
                                  filename=filename)
        return attachment

I'm not sure if the "smart_str(content, settings.DEFAULT_CHARSET)" part should also reference "encoding" rather than "settings.DEFAULT_CHARSET" but that's the message body handling text is written (django.core.mail.EmailMessage.message).

As I said, I don't have Outlook so I can't actually test the Outlook aspect but it does seem to change the charset to iso-8859-1 for both parts.

moberley
I don't have an Outlook to test either, but +1 for effort!
Van Gale
Your first instinct - that I shouldn't be messing with content_subtype - was correct. Removing that from my function correctly renders the HTML in outlook 2003 and the other e-mail clients as well.I think the Django documentation on sending e-mails might benefit from either removing mention to content_subtype or clarifying how to use it. I assumed that since Outlook could handle an alternative content type; I should use the content_subtype = 'html'. Obviously that is not the case and is contrary to what I was trying to achieve. Thanks so much for the sanity check.
celopes