How to Email a Django FileField as an Attachment?
Asked Answered
H

4

12

I need to send a models.FileField as an email attachment using Django. I've seen snippets that show how to do this with the raw request.FILES data (which still contains the Content-Type), but have not been able to find anything that shows how to do it once you've already saved the file in a models.FileField. The content type seems to be inaccessible from the models.FileField.

Can someone give me an example of how this would work? I'm beginning to think that I might have to store the Content-Type in the model when I save the file.

Thanks!

Hebert answered 28/6, 2011 at 1:23 Comment(1)
Could you provide the solution you found as an answer? I'd like to see the snippet too.Barbary
T
1

I would just not supply a content type and let the recipient's email client work it out. Unless it will be something unusual it shouldn't be a problem.

RFC2616 states:

If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource.

but... If you want to specify it then storing the content type on upload is a very good idea. It should be noted that django's own docs say to verify the data from users

If you are on a *unix OS you could try to guess/inspect it:

import subprocess
subprocess.check_output(['file', '-b', '--mime', filename]) 

(from How to find the mime type of a file in python? )

Tropic answered 28/6, 2011 at 4:10 Comment(1)
Thanks! I'll give this a go and report back!Hebert
D
19

Attaching a models.FileField file to an email message is nice and simple in Django:

from django.core.mail import EmailMultiAlternatives
kwargs = dict(
    to=to,
    from_email=from_addr,
    subject=subject,
    body=text_content,
    alternatives=((html_content, 'text/html'),)
)
message = EmailMultiAlternatives(**kwargs)
message.attach_file(model_instance.filefield.path)
message.send()
Dethrone answered 21/11, 2011 at 22:23 Comment(0)
V
8

I am using django-storages and so .path raises

NotImplementedError: This backend doesn't support absolute paths.

To avoid this, I simply open the file, read it, guess the mimetype and close it later, but having to use .attach instead of .attach_file magic.

from mimetypes import guess_type
from os.path import basename


f = model.filefield
f.open()
# msg.attach(filename, content, mimetype)
msg.attach(basename(f.name), f.read(), guess_type(f.name)[0])
f.close()
Vision answered 30/1, 2019 at 7:42 Comment(1)
It seems odd that the django mail module doesn't provide a better interface to the storage backend. This is the safest answer to work with multiple backends and is the one you need if you aren't using a filesystem backend. The question asked is how to email a django filefield as an attachment, but the answers that depend on the file being on a filesystem storage are really just answering the question for that filesystem storage backend configuration.Substantive
P
6

Another approach:

from django.core.mail.message import EmailMessage

msg = EmailMessage(subject=my_subject, body=my_email_body, 
      from_email=settings.DEFAULT_FROM_EMAIL, to=[to_addressed])
msg.attach_file(self.my_filefield.path) # self.my_filefield.file for Django < 1.7
msg.send(fail_silently=not(settings.DEBUG))
Pointsman answered 13/6, 2012 at 16:33 Comment(1)
I had to use msg.attach_file(self.my_filefield.path) with Django 1.7.1Nonscheduled
T
1

I would just not supply a content type and let the recipient's email client work it out. Unless it will be something unusual it shouldn't be a problem.

RFC2616 states:

If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource.

but... If you want to specify it then storing the content type on upload is a very good idea. It should be noted that django's own docs say to verify the data from users

If you are on a *unix OS you could try to guess/inspect it:

import subprocess
subprocess.check_output(['file', '-b', '--mime', filename]) 

(from How to find the mime type of a file in python? )

Tropic answered 28/6, 2011 at 4:10 Comment(1)
Thanks! I'll give this a go and report back!Hebert

© 2022 - 2024 — McMap. All rights reserved.