Python:如何使用 Django 发送电子邮件
在本篇文章中,我们将介绍如何使用 Django 发送电子邮件。我们将介绍如何配置 Django SMTP 连接,如何为你的电子邮件提供商设置应用程序密码,以及如何通过 Django shell 发送电子邮件。我们还将研究如何为你的 Django 应用程序设置联系表单,这将允许你的客户与你联系。
大多数 Web 应用程序使用电子邮件来管理关键操作,例如重置密码、帐户激活、接收客户反馈、发送新闻通讯和营销活动。这些任务中的大多数都需要像SendGrid
或Mailgun
这样的专用服务。但是,如果你不希望你的网站获得大量访问者,你实际上可以通过你的个人电子邮件提供商完成很多工作。
了解 SMTP
SMTP(或简单邮件传输协议)是一组用于确定电子邮件如何从发件人传输到收件人的规则。SMTP 服务器使用此协议来发送和中继外发电子邮件。(请注意,其他协议管理电子邮件的接收方式。)
SMTP 服务器始终具有唯一的地址和用于发送消息的特定端口,在大多数情况下为587。我们将看到在使用 Django 发送电子邮件时端口是如何相关的。
由于我们将使用 Gmail,因此我们将使用的地址是smtp.gmail.com
,端口是 587
。
现在让我们看看如何使用 Django 发送电子邮件。
创建 Django 项目
每个 Django 项目都应该有一个虚拟环境,因为我们不想弄乱项目依赖项。要创建一个,请运行以下命令:
python -m venv .venv
上面的命令创建了一个名为 的虚拟环境.venv。要激活此虚拟环境,您可以使用以下命令:
source .venv/bin/activate
由于 Django 是第三方包,你必须用 pip 安装它:
pip install django
这将安装最新版本的 Django,你可以使用pip freeze.
要创建 Django 项目,请调用命令行实用程序django-admin
:
django-admin startproject EmailProject
使用上面的命令,你正在创建一个名为 的 Django 项目EmailProject
,但你可以使用任何你想要的名称创建该项目。
现在,进入项目目录并运行服务器:
cd EmailProject
python manage.py runserver
运行 Django 服务器后,在浏览器中访问http://localhost:8000。你将看到一个自动生成的页面,其中包含最新的 Django 发行说明。
修改设置
你需要在发送电子邮件之前修改设置文件,因此让我们使用以下命令找到该文件tree:
注意:为简单起见,我们将仅使用 UNIX(macOS 或 Linux)系统命令。
tree
该tree命令输出目录的文件结构。在这种情况下,由于我们没有给它一个特定的目录路径,如果我们在项目的根文件夹中,我们将得到类似于以下的内容:
├── EmailProject
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
1 directory, 6 files
我们将在本教程中不断修改的文件是EmailProject
文件夹中的settings.py
。
settings.py
包含您需要的所有项目配置,并允许你设置自定义变量。正如 Django 文档所说,“设置文件只是一个带有模块级变量的 Python 模块”。
让我们看一下使用 Django 发送电子邮件所需的设置。打开EmailProject/settings.py
文件并将以下设置粘贴到文件底部:
# EmailProject/settings.py
# Bottom of the file
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = ''
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
让我们通过分析这些设置中的每一个来分解上面的代码。
电子邮件后端
该EMAIL_BACKEND
设置声明后端我们的Django项目将使用与SMTP服务器连接。
此变量指向smtp.EmailBackend
接收发送电子邮件所需的所有参数的类。我强烈建议你直接在Django 源代码上查看类构造函数。你可能会惊讶于这段代码的可读性。
注意:虽然这个类是默认的
EMAIL_BACKEND
,但在 Django 设置中显式被认为是一个好习惯。
所有其他电子邮件设置将基于此EmailBackend
类的构造函数。
电子邮件主机
该EMAIL_HOST
设置指的是你将使用的 SMTP 服务器域。这取决于你的电子邮件提供商。下表列出了与三个常见提供商对应的 SMTP 服务器主机:
电子邮件提供商 | SMTP 服务器主机 |
---|---|
Gmail | smtp.gmail.com |
Outlook/Hotmail | smtp-mail.outlook.com |
雅虎 | smtp.mail.yahoo.com |
我们暂时将此设置留空,因为我们稍后将使用.env
文件来避免硬编码敏感密钥或每个站点的配置。你永远不应该将凭据直接设置到代码中。
我们将使用Django Environ
来解决这个问题。
电子邮件端口
该EMAIL_PORT
设置必须设置为587
,因为它是大多数 SMTP 服务器的默认端口。对于个人电子邮件提供商而言,情况仍然如此。
此端口与 TLS 加密一起使用,以确保电子邮件发送的安全性。
电子邮件使用 TLS
传输层安全(TLS) 是一种跨 Web 使用的安全协议,用于加密 Web 应用程序 (Django) 和服务器 (SMTP 服务器) 之间的通信。
最初,我们将EMAIL_USE_TLS
变量设置为True
。这意味着 Django 将使用传输层安全连接到 SMTP 服务器并发送电子邮件。(对于个人电子邮件提供商,这是强制性的。)
电子邮件主机用户
EMAIL_HOST_USER
设置是你的个人电子邮件地址。现在将其留空,因为我们将使用它django-environ
来设置所有这些凭据。
电子邮件主机密码
EMAIL_HOST_PASSWORD
设置是你将从你的电子邮件帐户中获得的应用密码 - 我们将在本节之后立即执行此过程。
同样:将此设置留空,因为我们稍后将使用环境变量。
在 Gmail 中设置应用密码
要使用EMAIL_HOST_PASSWORD
设置,你需要激活安全性较低的应用访问权限,并从你的个人电子邮件地址获得应用密码。
如果你不激活安全性较低的应用程序访问权限,你可能会得到一个SMTPAuthenticationError
,因为 Django 无法遵守 Google 安全协议。
你可以选择使用普通密码,但这比使用应用密码风险更大。我的建议是创建一个新的 Gmail 帐户或使用“测试”电子邮件地址。
考虑到这一点,你可以通过以下步骤获取Gmail
应用密码。请注意,如果你使用的是现有帐户并启用了两步验证,则可以跳过第 2 步和第 3 步:
- 创建或登录 Gmail 帐户
- 转到myaccount.google.com/lesssecureapps并打开安全性较低的应用程序选项。
- 启用双因素身份验证,因为需要获取应用密码。
- 现在你已启用双因素身份验证,是时候获取应用密码了。为此,你可以转到Google 帐户的安全部分,向下滚动到登录 Google 部分,然后点击应用密码。
在被重定向到应用程序密码页面之前,你需要重新提示您的密码(帐户密码)。
进入后,单击select app
,你将为该应用程序密码选择一个自定义名称 - 例如“Django Send Email
” - 然后单击GENERATE
。
将显示一个新窗口,其中包含一个 16
个字符的密码。复制它,因为我们需要它来配置我们的 Django 项目。
使用 Django Environ 隐藏敏感键
即使你只是在开发中发送电子邮件,也不应该将密码直接写入源代码。当使用版本控制系统和 GitHub 来托管你的项目时,这一点变得更加重要。你不希望人们访问你的数据。
让我们看看如何通过使用Django-environ
来防止这种情况发生。
使用以下命令.env
在EmailProject
目录(settings.py
文件所在的位置)内创建一个文件:
cd EmailProject/
ls
settings.py # The settings file must be here
touch .env
现在,打开该.env
文件并输入以下键值对:
EMAIL_HOST=smtp.gmail.com
EMAIL_HOST_USER=YourEmail@address
EMAIL_HOST_PASSWORD=YourAppPassword
RECIPIENT_ADDRESS=TheRecieverOfTheMails
分解此文件的内容:
-
EMAIL_HOST
:你的电子邮件提供商 SMTP 服务器地址。请参阅上面的电子邮件主机表以获取快速指导。在本例中,我使用的smtp.gmail.com
是 Gmail SMTP 地址。 -
EMAIL_HOST_USER
:你的电子邮件地址。 -
EMAIL_HOST_PASSWORD
:你刚刚生成的应用密码。请记住,它不包含任何空格。 -
RECIPIENT_ADDRESS
:你将收到邮件的电子邮件地址。这是我们稍后将创建的自定义设置,用于将所有电子邮件发送给同一收件人。
要使用这些环境变量,我们需要安装Django-environ
:
pip install django-environ
注意:确保你的虚拟环境已激活。
现在,打开settings.py
位于EmailProject
目录中的 并使用以下代码:
# EmailProject/settings.py
# This should be at the start of the file
import environ
env = environ.Env()
environ.Env.read_env()
# Previous settings ...
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = env('EMAIL_HOST')
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
# Custom setting. To email
RECIPIENT_ADDRESS = env('RECIPIENT_ADDRESS')
首先,我们在environ
设置文件的顶部导入包。请记住,所有导入都应在开头。
然后我们创建一个env
变量,该变量将包含.env
.
env('KEY')
语句意味着我们正在查找该键的值。确保.env
在继续之前设置了文件,因为如果ImproperlyConfigured
未设置某些环境变量,你将收到 Django错误。
请注意,这RECIPIENT_ADDRESS
是一个自定义设置,我们将使用它来将电子邮件发送到我们可以访问的地址。
如果你使用 Git 和 GitHub .env,请不要忘记在.gitignore
中包含该文件。你只需打开它并添加以下行即可完成此操作:
.env
1. 使用 Django Shell 发送电子邮件
最后,我们进入了文章的精彩部分!是时候向 Django 发送你的第一封电子邮件了。
打开终端,激活虚拟环境,然后运行:
python manage.py shell
这将创建一个 shell,其中包含已为我们配置的所有 Django 设置。在那个全新的 shell 中,粘贴以下代码:
>>> from django.core.mail import send_mail
>>> from django.conf import settings
>>> send_mail(
... subject='A cool subject',
... message='A stunning message',
... from_email=settings.EMAIL_HOST_USER,
... recipient_list=[settings.RECIPIENT_ADDRESS])
1
我们也可以在不指定参数的情况下制作单行:
>>> send_mail('A cool subject', 'A stunning message', settings.EMAIL_HOST_USER, [settings.RECIPIENT_ADDRESS])
1
让我们分解上面的代码:
- 我们导入 Django
send_mail
函数。 - 然后我们导入settings包含所有全局设置和每个站点设置(settings.py文件内的设置)的对象。
- 最后,我们将所有需要的参数传递给
send_mail
函数。此函数返回发送的电子邮件数量,在本例中为1。
请注意我们如何使用settings对象来获取from_email
(您用来发送电子邮件的电子邮件)和recipient_list
(我们在RECIPIENT_ADDRESS
中定义的自定义设置.env
)。
现在,如果我检查我的收件箱——当我将RECIPIENT_ADDRESS
环境变量设置为我的电子邮件地址时——我将收到 Django 发送的消息。
2. 使用 Django 构建自动联系表单
在本节中,我们将使用 Django 表单和内置send_mail
函数构建一个自动联系表单。此外,我们将send()
在联系表单中创建一个自定义函数 ,以便在视图中更容易实现它。
让我们从创建联系人应用程序开始。进入项目根目录——所在manage.py的
位置——并运行:
python manage.py startapp contact
然后,将其安装在文件INSTALLED_APPS
内的变量中EmailProject/settings.py
:
# EmailProject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
...
# Custom
'contact',
]
在推进contact应用程序之前,让我们配置文件的urlpatterns
内部EmailProject/urls.py
。为此,请导入django.urls.include
函数并在整个项目中包含联系人 URL。别担心;我们稍后将配置联系人 URL:
# EmailProject/urls.py
from django.contrib import admin
from django.urls import path, include # New import
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('contact.urls')) # Include Contact URLs
]
联系表
进入contact应用程序文件夹并创建一个forms.py
文件。在forms.py
文件中定义所有表单是一种很好的做法,但这不是强制性的。这就是 Django 默认不包含这个文件的原因。
你可以使用以下命令创建表单文件:
cd ../contact/
# You were inside the EmailProject folder
touch forms.py
打开你刚刚创建的文件并进行以下导入:
# contact/forms.py
from django import forms
from django.conf import settings
from django.core.mail import send_mail
Django表单模块为我们提供了创建联系表单所需的所有类和字段。我们再次导入settings
对象和send_mail
发送电子邮件的函数。
我们的联系表单将包含多个字段并使用两种自定义方法:get_info()
,用于格式化用户提供的信息,以及send()
,将发送消息。
让我们看看这在代码中的实现:
# contact/forms.py
class ContactForm(forms.Form):
name = forms.CharField(max_length=120)
email = forms.EmailField()
inquiry = forms.CharField(max_length=70)
message = forms.CharField(widget=forms.Textarea)
def get_info(self):
"""
Method that returns formatted information
:return: subject, msg
"""
# Cleaned data
cl_data = super().clean()
name = cl_data.get('name').strip()
from_email = cl_data.get('email')
subject = cl_data.get('inquiry')
msg = f'{name} with email {from_email} said:'
msg += f'\n"{subject}"\n\n'
msg += cl_data.get('message')
return subject, msg
def send(self):
subject, msg = self.get_info()
send_mail(
subject=subject,
message=msg,
from_email=settings.EMAIL_HOST_USER,
recipient_list=[settings.RECIPIENT_ADDRESS]
)
这是一个巨大的类,所以让我们分解我们在每个部分中所做的事情。首先,我们定义了发送消息所需的四个字段:
-
name
和enquiry
是表示联系消息的名称和原因的CharFields
。 -
email
是一个EmailField
,代表试图与您联系的人的电子邮件。考虑到电子邮件不会由用户的电子邮件地址发送,而是由您在 Django 项目中设置的发送电子邮件的电子邮件地址发送。 -
message
是另一个CharField
例外,我们使用的是Textarea
小部件。这意味着,当显示表单时,它将呈现一个<textarea>
标签而不是一个简单的<input>
.
进入自定义方法,我们只使用该get_info
方法来格式化用户提供的信息并返回两个变量:subject
,它只是inquiry
字段,以及message
,它将是 Django 发送的实际消息。
另一方面,该send()
方法仅从函数中获取格式化信息get_info
并发送消息send_mail
。
尽管本节非常大,但你将看到我们如何通过将所有发送逻辑实现到ContactForm
自身来简化联系人视图。
联系人视图
打开contact/views.py
文件并添加以下导入:
# contact/views.py
from django.views.generic import FormView, TemplateView
from .forms import ContactForm
from django.urls import reverse_lazy
如你所见,我们将使用Django 通用视图,这在执行简单任务时为我们节省了大量时间——例如,在FormView
使用TemplateView
.
此外,我们正在导入ContactForm
我们在上一节中构建的 以及在使用基于类的视图时使用的reverse_lazy
函数。
继续查看视图,让我们编写ContactView
:
# contact/views.py
class ContactView(FormView):
template_name = 'contact/contact.html'
form_class = ContactForm
success_url = reverse_lazy('contact:success')
def form_valid(self, form):
# Calls the custom send method
form.send()
return super().form_valid(form)
如你所见,我们正在使用我们创建的构建一个简单的FormView
ContactForm
。我们也在设置template_name
和success_url
。我们稍后将编写 HTML 模板并设置 URL。
该形式有效的方法,让我们用发送电子邮件ContactForm.send()
只有在形式的所有字段都是有效的方法。这意味着如果用户输入无效的输入——例如未格式化的电子邮件地址——消息将不会被发送。
form_valid
在基于函数的视图中,上述方法实现将等效于以下内容:
# Previous function based contact_view ...
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form.send()
return redirect('contact:success')
else:
form = ContactForm())
在本节结束时,我们将编写一个ContactSucessView
,它将向用户显示一条成功消息。由于我们已经导入了TemplateView
类,我们只需要继承它并定义template_name
属性:
# contact/views.py
class ContactSuccessView(TemplateView):
template_name = 'contact/success.html'
联系网址
是时候创建contact应用程序的 URL 模式了。由于 Djangourls.py
默认不给我们文件,我们需要使用以下命令创建它(确保在contactapp
文件夹中):
pwd
# /path/to/project/EmailProject/contact
touch urls.py
打开该文件并设置app_name
和urlpatterns
变量:
from django.urls import path
from .views import ContactView, ContactSuccessView
app_name = 'contact'
urlpatterns = [
path('', ContactView.as_view(), name="contact"),
path('success/', ContactSuccessView.as_view(), name="success"),
]
我们使用路径将路由及其对应视图包含到应用程序的 URL 配置中。当我们将app_name变量设置为 时'contact',这意味着应用程序的 URL命名空间将如下所示:
contact:name_of_path
# For ContactView
contact:contact
# For ContactSuccessView
contact:success
注意:命名空间是我们在 Django 模板和视图中动态调用的 URL。
你可以在官方文档中了解有关 Django URL 调度程序的更多信息。
编写模板
Django 模板是动态显示数据的首选方式,使用HTML和 Django 模板语言给我们的特殊标签。
对于这个特定的应用程序,我们将使用三个模板:
-
base.html
: 所有其他模板都将从它继承。它将包含所有模板必须具有的所有 HTML 框架,以及指向 Bootstrap 的链接。 -
contact.html
:显示联系表格。 -
success.html
: 显示成功消息。
让我们从创建联系人的应用程序模板结构开始(确保你在联系人应用程序文件夹中):
mkdir -p templates/contact/
cd templates/contact
touch base.html contact.html success.html
上面的命令创建了可重用 Django 应用程序的典型模板结构appname/templates/appname
——以及我之前提到的树模板文件。
应用程序文件结构现在应该如下所示:
.
├── admin.py
├── apps.py
├── forms.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── templates
│ └── contact
│ ├── base.html
│ ├── contact.html
│ └── success.html
├── tests.py
├── urls.py
└── views.py
让我们进入base.html
模板的内容:
<!-- contact/templates/contact/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Django Email Send</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous" />
</head>
<body>
{% block body %}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous">
</script>
</body>
</html>
如你所见,它是包含Bootstrap 5链接的 HTML 文件的简单框架。这允许我们在不使用 CSS 文件的情况下对我们的联系人应用程序进行风格化。
{% block name-of-block %}
标签允许我们设置一个“子模板”将使用的占位符。使用此标记使模板继承成为一项简单的任务。
在进入表单之前,你需要安装Django 脆皮表单包,它允许我们轻松地对它们进行样式化:
pip install django-crispy-forms
再一次,crispy_forms
是一个 Django 应用程序,我们需要将它包含在INSTALLED_APPS
列表中:
# config/settings.py
INSTALLED_APPS = [
...
# 3rd party apps
'crispy_forms',
# Custom apps
'contact',
]
# Indicates the frontend framework django crispy forms use
CRISPY_TEMPLATE_PACK = 'bootstrap4'
我们使用 Bootstrap 4 的模板包,因为 Bootstrap 表单类在第 4 版和第 5 版之间兼容(在撰写本文时)。
现在,让我们处理contact.html
模板:
<!-- contact/templates/contact/contact.html -->
{% extends 'contact/base.html' %}
{% load crispy_forms_tags %}
{% block body %}
<div class="mx-auto my-4 text-center">
<h1>Contact Us</h1>
</div>
<div class="container">
<form action="" method="post">
{% csrf_token %}
{{ form | crispy }}
<button class="btn btn-success my-3" type="submit">Send message</button>
</form>
</div>
{% endblock %}
请注意我们如何扩展基本模板并使用块占位符。这就是 Django 模板语言如此高效的原因,因为它让我们节省了大量的 HTML 复制和粘贴。
谈到表单,我们使用了方法"post
",这意味着我们ContactView将处理用户提供的数据,如果表单有效,我们将发送电子邮件。
{% csrf_token %}
是由于安全原因,所有形式的强制性。Django 的文档有一个关于CSRF 令牌的专用页面以及在处理表单时使用它们的原因。
我们将使用crispy模板标签渲染表单,这就是为什么我们用{% load crispy_forms_tags %}
.
最后,让我们编写success.html
模板:
{% extends 'contact/base.html' %}
{% block body %}
<div class="mx-auto my-4 text-center">
<h1 class="fw-bolder text-success">We sent your message</h1>
<p class="my-5">You can send another in the <a href="{% url 'contact:contact' %}">contact page</a></p>
</div>
{% endblock %}
如你所见,这是一个简单的成功公告,其中包含指向联系表单的链接,以防用户想要发送另一条消息。
让我们再次运行服务器并访问http://localhost:8000(确保你已.venv
激活并且你在项目根文件夹中):
python manage.py runserver
下图显示了最终联系表格的样子。
这是成功消息的图像。
这是收件箱中电子邮件的图像。
总结
恭喜!你已经学习了如何使用 Django 发送电子邮件以及如何构建 Django 联系表单。
使用 Django 发送电子邮件的方法有很多种。在本教程中,你已使用个人电子邮件地址完成此操作,但我希望你探索其他工具并将它们集成到你的项目中。
在本篇文章中,我们介绍了以下内容:
- 如何设置 Django 设置以提供电子邮件
- 小项目中如何使用个人邮箱发送邮件
- 如何.env在 Django 项目中使用文件来使用敏感数据
- 如何构建自动联系表单