For ones who seek the full solution to the question, that will work as intended on all email clients including iOS default email app.
When to use multipart/mixed
from the RFC
The primary subtype for multipart, "mixed", is intended for use when the body parts are independent and intended to be displayed serially. Any multipart subtypes that an implementation does not recognize should be treated as being of subtype "mixed".
so multipart/mixed
type should be used when all of the parts of the message (text/html
, text/plain
, image/png
etc.) are equally important and all should be displayed to the user
this means that if you attach text/html
to the message and also text/plain
as a fallback to html, both of them will be shown to the user one over the other which doesn't make any sense
When to use multipart/alternative
from the RFC
In particular, each of the parts is an "alternative" version of the same information. User agents should recognize that the content of the various parts are interchangeable. The user agent should either choose the "best" type based on the user's environment and preferences, or offer the user the available alternatives. In general, choosing the best type means displaying only the LAST part that can be displayed. This may be used, for example, to send mail in a fancy text format in such a way that it can easily be displayed anywhere
This means that whatever you attach to the multipart/alternative
message can be treated as the same value but in different forms.
Why is that important?
if after the textual parts, you attach an image (image/png
, image/jpeg
) to the multipart/alternative
message it can be treated as equally important as the message itself thus the only thing that the user will see is the image - without the text/html
nor text/plain
parts.
This is not true for most of nowadays clients - they are smart enough to know what you mean, but the one that still does this is iOS default Mail app - you don't want to confuse those users.
Whats the correct way to do it then?
After the explanation this will make much more sense now
# keeps the textual and attachment parts separately
root_msg = MIMEMultipart('mixed')
root_msg['Subject'] = email.subject
root_msg['From'] = self._format_from_header()
root_msg['To'] = self._format_addrs(recipients=email.recipients)
alter_msg = MIMEMultipart('alternative')
plain_text = MIMEText('some text', 'plain', 'UTF-8')
html = MIMEText('<strong>some text</strong>', 'html')
alter_msg.attach(plain_text)
alter_msg.attach(html)
# now the alternative message (textual)
# is just a part of the root mixed message
root_msg.attach(alter_msg)
img_path = '...'
filename = os.path.basename(img_path)
with open(img_path, 'rb') as f:
attachment = MIMEImage(f.read(), filename=filename)
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
# now the images (attachments) will be shown alongside
# either plain or html message and not instead of them
root_msg.attach(attachment)