Python:如何使用 Django 发送电子邮件

2021-09-10 11:47:02 浏览数 (4998)

在本篇文章中,我们将介绍如何使用 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 发行说明。

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 服务器主机
Gmailsmtp.gmail.com
Outlook/Hotmailsmtp-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 步:

  1. 创建或登录 Gmail 帐户
  2. 转到myaccount.google.com/lesssecureapps并打开安全性较低的应用程序选项。不太安全的应用
  3. 启用双因素身份验证,因为需要获取应用密码。2 因素验证
  4. 现在你已启用双因素身份验证,是时候获取应用密码了。为此,你可以转到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 发送的消息。

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 项目中使用文件来使用敏感数据
  • 如何构建自动联系表单