How to Send Email With Attachments in Django

Django Email Attachment comes with in-built, ready to use Email System. Sending Email using Django is Really Simple Task. In Django, we just need to import django.core.mail.

In this tutorial, you will learn how to send email with multiple attachments in Django. You will also learn how to send email with a single attachment. Here, I will explain how to setup your SendGrid Account for Sending Emails for your Django Application. I will also explain how to set your Email Host in the settings.py file, in your application:

  • EMAIL_HOST − SMTP server.
  • EMAIL_HOST_USER − Login credentials for the SMTP server.
  • EMAIL_HOST_PASSWORD − Password credential for the SMTP server.
  • EMAIL_PORT − SMTP server port.
  • EMAIL_USE_TLS or _SSL − True if secure connection.

Lets get started by setting up SendGrid Account Free Plan.

Setup SendGrid Account for Email API

Go to the SendGrid Website. On the homepage, you will find Try For Free Button. It will take you to the Create Account Page. After creating your account, you should activate your account. Activation Email is sent on Register Email Address. Click on the link and it will activate your Account.

Create SendGrid API Key

  1. In the Sendgrid dashboard, head to Settings -> API Keys and click on ‘Create API Key’.
  2. Next, name your API key and make sure to set permissions to Full Access. Click ‘Create & View’ when you are ready.
  3. Next, click on the API key to copy it to your clipboard.

Configure Django for SendGrid SMTP server

Go to your settings.py file and add the following parameters

# Email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIT_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'apikey'
EMAIL_HOST_PASSWORD = 'SG.oDN9basdaECvH5asdasw.gXVEgtD1asqSkn-EW'

Please change EMAIL_HOST_PASSWORD with your SendGrid Password.

Note: It is risky to directly put sensitive data directly in settings.py. For putting sensitive data, we should you Environment Variable or Python Decouple. For the testing purpose, this is absolutely fine.

Sending Emails from your Django Applications

Setup your initial Django Project. I have a tutorial where I install Django Application in Virtual Environment. Once your setup your initial project then we can go further. I hope you people have already created because above you have edited your settings.py file.

I am also sharing GitHub link of the project at the end of blog. The bellow is the final application will look like:

How to Send Email With Multiple Attachments in Django

Here I am sharing Views, Forms, HTML file, and URLs Snippet. The actual email is sent because we have imported from django.core.mail import EmailMessage.

This the main logic which sends email in our Django App.

mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
mail.attach(attach.name, attach.read(), attach.content_type)
mail.send()

Views.py

# views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from django.core.mail import EmailMessage

from django.conf import settings
from .forms import EmailForm

class EmailAttachementView(View):
    form_class = EmailForm
    template_name = 'emailattachment.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class()
        return render(request, self.template_name, {'email_form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST, request.FILES)

        if form.is_valid():
            
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']
            email = form.cleaned_data['email']
            files = request.FILES.getlist('attach')

            try:
                mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
                for f in files:
                    mail.attach(f.name, f.read(), f.content_type)
                mail.send()
                return render(request, self.template_name, {'email_form': form, 'error_message': 'Sent email to %s'%email})
            except:
                return render(request, self.template_name, {'email_form': form, 'error_message': 'Either the attachment is too big or corrupt'})

        return render(request, self.template_name, {'email_form': form, 'error_message': 'Unable to send email. Please try again later'})

    # Single File Attachment
    # def post(self, request, *args, **kwargs):
    #     form = self.form_class(request.POST, request.FILES)

    #     if form.is_valid():
            
    #         subject = form.cleaned_data['subject']
    #         message = form.cleaned_data['message']
    #         email = form.cleaned_data['email']
    #         attach = request.FILES['attach']

    #         try:
    #             mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
    #             mail.attach(attach.name, attach.read(), attach.content_type)
    #             mail.send()
    #             return render(request, self.template_name, {'email_form': form, 'error_message': 'Sent email to %s'%email})
    #         except:
    #             return render(request, self.template_name, {'email_form': form, 'error_message': 'Either the attachment is too big or corrupt'})

    #     return render(request, self.template_name, {'email_form': form, 'error_message': 'Unable to send email. Please try again later'})

The above comment code post is for sending a mail with a single attachment.

Forms.py

# forms.py
from django import forms

class EmailForm(forms.Form):
    email = forms.EmailField()
    subject = forms.CharField(max_length=100)
    attach = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
    message = forms.CharField(widget = forms.Textarea)

This the form which we have displayed on our webpage. I am using widget=forms.ClearableFileInput(attrs={'multiple': True}) for multiple attachments. If you are using a single attachment email then remove that line.

emailattchment.html

{% load crispy_forms_tags %}

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Email Attachment - Django and Python</title>
  <!-- Bootstrap core CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>

<body>

  <!-- Page Content -->
  <div class="container">
    <div class="row">
      <div class="col-lg-12">
        <h1 class="mt-5 mb-3">Django Email Attachment</h1>

          {% if error_message %}
          <div class="alert alert-primary" role="alert">
            {{error_message}}
          </div>
          {% endif %}

          {% if email_form %}
          <form method="POST" action ="" enctype="multipart/form-data">
            {% csrf_token %}
            {{email_form|crispy}}
          <button type="submit" class="btn btn-primary">Send Email</button>
          </form>
          {% endif %}

      </div>
    </div>
  </div>

  <!-- Bootstrap core JavaScript -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

</body>

</html>

I am using Bootstrap4 and django_crispy_form library for good UI.

Urls.py

from django.urls import path
from emailattachment.views import EmailAttachementView

urlpatterns = [
    path('', EmailAttachementView.as_view(), name='emailattachment')
]

Route to display our view.

Conclusion

The above code is able to send attachments like images, files via emails. We have shared the code which will send single and multiple email attachments at the same time. Run your Django Server and go to http://localhost:8000/

This is also available on GitHub – https://github.com/studygyaan/how-to-send-email-with-attachments-in-django

Share