Wouldn't it be great if you can automatically send emails to users on your website? How about sending emails for free? With leveraging our free Gmail account, you can send emails from our Django website! In this tutorial, we'll learn how to send emails using Gmail and Django. As a test, we'll be sending an email to ourselves from our site.
1. Project Setup
Lets setup our normal Django project. In your terminal, enter the following command:
$ django-admin startproject sendemail
$cd sendmail
$ python manage.py startapp emailapp
2. Edit your settings.py and urls.py files
We need to configure our settings before we continue. Inside of sendemail/settings.py, plug in the newly created app into INSTALLED_APPS:
# sendemail/settings.pyINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','emailapp',]
At the bottom of the settings file, we'll setup our Django project to utilize Gmail email backend. (We'll update the user and password fields later in this tutorial). Enter the following:
Open sendemail/urls.py. Lets configure our project's url patterns. Enter the following:
3. Creating our views
Our main functions are going to render and handle a contact form submit, and execute Django's 'sendmail" method. Copy and paste the following in your views.py:
# sendemail/emailapp/views.pyfrom django.shortcuts import renderfrom django.template.loader import render_to_stringfrom django.core.mail import send_mailfrom django.contrib import messagesfrom .forms import ContactFormfrom django.conf import settingsdef contact(request):if request.method == 'POST':form = ContactForm(request.POST)if form.is_valid():email = form.cleaned_data.get('email')name = form.cleaned_data.get('name')message = form.cleaned_data.get('message')email_inquiry(name=name, email=email, message=message, subject="Lab Tutorials")messages.success(request, message="Email was sent successfully!")return render(request, 'emailapp/contact.html', {'form':form,})else:messages.error(request, "Error processesing emails, please try again")return render(request, 'emailapp/contact.html', {'form':form,})else:form = ContactForm()if 'submitted' in request.GET:submitted = Truereturn render(request, 'emailapp/contact.html', {'form':form,})def email_inquiry(name, email, message, subject):msg_plain = render_to_string('emailapp/email_inquiry.txt', {'contactName':name, 'contactEmail':email, 'contactMessage':message,})msg_html = render_to_string('emailapp/email_inquiry.html', {'contactName':name, 'contactEmail':email, 'contactMessage':message,})send_mail(subject=subject,message=msg_plain,from_email=settings.EMAIL_HOST_USER, recipient_list=[settings.EMAIL_HOST_USER], html_message=msg_html)
4. Setting up our form
Inside your app, create a file named forms.py. We would want to have a separate file for configuring our form. Paste the following in the file:
# sendemail/emailapp/forms.pyfrom django import formsfrom django.forms import ModelFormclass ContactForm(forms.Form):name = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'form-control'}))email = forms.EmailField(label="Your email address", required=True, widget=forms.EmailInput(attrs={"class": "form-control"}))message = forms.CharField(label="Type your message...", required=True, widget=forms.Textarea(attrs={'class': 'form-control', 'rows':'5'}))
5. Setup our app's url patterns
Create a new file within our app named urls.py. Paste the following in the file:
# sendemail/emailapp/urls.pyfrom django.urls import pathfrom django.conf import settingsfrom . import viewsfrom django.conf.urls.static import staticurlpatterns = [path('', views.contact, name='contact'),]
6. Templates
Inside your app's folder, create another folder: templates/emailapp. Inside this new folder, create 5 files:
Copy and paste the following for each file:
base.html
{# HTML5 declaration #}<!doctype html><html lang="en"><head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">{% load static %}<!-- Bootstrap 5 CDN --><!-- Latest compiled and minified CSS --><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"><!-- Latest compiled JavaScript --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script><title>{% block title %}Follow me @heyylateef{% endblock title %}</title><header>{# Navigation Menu #}</header></head><body>{# The Document Body #}<div class = "container">{% if messages %}{% for message in messages %}{% if message.tags == 'success'%}<div class="alert alert-success alert-dismissible"><button type="button" class="btn-close" data-bs-dismiss="alert"></button><strong>{{message}}</strong></div>{% elif message.tags == 'info'%}<div class="alert alert-info alert-dismissible"><button type="button" class="btn-close" data-bs-dismiss="alert"></button><strong>{{message}}</strong></div>{% elif message.tags == 'warning'%}<div class="alert alert-warning alert-dismissible"><button type="button" class="btn-close" data-bs-dismiss="alert"></button><strong>{{message}}</strong></div>{% elif message.tags == 'error'%}<div class="alert alert-danger alert-dismissible"><button type="button" class="btn-close" data-bs-dismiss="alert"></button><strong>{{message}}</strong></div>{% endif %}{% endfor %}{% endif %}</div><div class="container">{% block content %}if you see this, something is wrong!{% endblock content %}</div>{# The Footer #}<div class="footer-basic"></div>{% block javascript %}{% endblock javascript %}</body></html>
contact.html
{% extends 'emailapp/base.html' %}{% load static %}{% block content %}<section class="page-section" id="about"><div class="container pt-5"><div class="row justify-content-center"><div class="col-lg-8 text-center"><h2 class="mt-0">Contact Form!</h2><hr class="divider light my-4" /><div class="col-lg-8 text-left"></div></div></div></div><div class="container pb-5">{% if submitted %}<p class="success">Your request was submitted successfully.</p>{% else %}<form action="" method="post" novalidate>{{form.as_table}}<div class="col pt-3"><input type="submit" class="btn btn-success" name="Submit"></div>{% csrf_token %}</form>{% endif %}</section>{% endblock %}
email_base.html
{# HTML5 declaration #}<!DOCTYPE html><html lang="en"><head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><!-- Latest compiled and minified CSS --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><!-- Font Awesome (free icons) --><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous"><title>{% block title %}Lateef Lab{% endblock title %}</title></head><body><style></style><header>{# Navigation Menu #}</header>{# The Document Body #}<div class = "container mt-5">{% if messages %}{% for message in messages %}{% if message.tags == 'success'%}<div class="alert alert-success alert-dismissible"><button type="button" class="close" data-dismiss="alert">×</button><strong>{{message}}</strong></div>{% elif message.tags == 'info'%}<div class="alert alert-info alert-dismissible"><button type="button" class="close" data-dismiss="alert">×</button><strong>{{message}}</strong></div>{% elif message.tags == 'warning'%}<div class="alert alert-warning alert-dismissible"><button type="button" class="close" data-dismiss="alert">×</button><strong>{{message}}</strong></div>{% elif message.tags == 'error'%}<div class="alert alert-danger alert-dismissible"><button type="button" class="close" data-dismiss="alert">×</button><strong>{{message}}</strong></div>{% endif %}{% endfor %}{% endif %}</div><div class="container-fluid p-0">{% block content %}<!--if you see this, something is wrong!-->{% endblock content %}</div>{# The Footer #}<!-- Optional JavaScript --><!-- jQuery first, then Popper.js, then Bootstrap JS --><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script><!-- Popper JS --><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script><!-- Latest compiled JavaScript --><script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script></body></html>
email_inquiry.html
{% extends "emailapp/email_base.html" %}{% block content %}<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><tbody><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">Here is a review of your inquiry:</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">Name: {{contactName}}</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">Email: {{contactEmail}}</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">{{contactMessage}}</td></tr><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"><b>Lateef Lab</b></td></tr></tbody></table></td></tr></tbody></table></div></tr></tbody></table>{% endblock content %}
email_inquiry.txt
7. Setup Gmail App Password
On your computer, go to gmail.com and login. Once logged in, on the top right corner of the window, click on your Gmail profile picture and click "Mange your Google Account"
Next, look at the left hand navigation menu. Click "Security", then "App passwords". Note: you will need to set up 2 step verification on your Gmail to have the ability to create app passwords.
Sign in again
In the 2-Step Verification page, scroll to the bottom and click on "App Passwords".
In the App Password window, click on the dropdown menu and select "Other". Then create a new for your new app password. Click "Generate".
Now, here is your app password! Copy and paste the app password in your settings.py file as the value of the EMAIL_HOST_PASSWORD variable. (Note: please keep this password safe, you should read this password as an environment variable)
8. Updating settings.py
Go back to your settings.py file, update the following variables with your gmail account and app password as strings:
9. Lets test our app!
Lets (finally) test our app! We'll run migrations and run it locally. In your terminal, enter the following commands:
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver
Go to localhost:8000. You'll see en empty form, go ahead and fill it out and submit it!
If your form was valid, you should notice the web page will reload and display a success message at the top of the screen.
At last, lets check our Gmail. You should see a similarly rendered email in your inbox (from yourself).
Like the content posted on this site? Need help with another web project? Want to give some feedback? Feel free to contact me via email or social media!
Know more!