如何使用Django发送电子邮件

在本教程中,我们将逐步介绍如何使用Django发送电子邮件。内容包括如何配置Django的SMTP连接,如何为您的电子邮件服务提供商设置应用密码,以及如何通过Django shell发送电子邮件。我们还将探讨如何为您的Django应用设置一个联系表单,使客户能够与您取得联系。

大多数网络应用依赖电子邮件处理关键操作,例如密码重置、账户激活、接收客户反馈、发送新闻简报及营销活动。这些任务大多需要如SendGridMailgun这样的专业服务。但如果您的网站预计访问量不大,其实可以通过个人电子邮件服务完成许多工作。

对于小型项目或测试项目,使用个人电子邮件发送邮件是一个合理的选择,因此本教程将采取这种方法以简化操作。然而,不建议在生产网站上使用个人电子邮件服务。您可以了解更多关于Gmail发送限制的信息,或参考您电子邮件服务提供商的限制。

注意:本教程的完整代码可在GitHub上获取。

了解SMTP

SMTP(即简单邮件传输协议)是一套规定电子邮件如何从发件人传输到收件人的规则。SMTP服务器利用此协议发送和中继外发邮件。(需要注意的是,接收邮件的规则由其他协议管理。)

每个SMTP服务器都有一个唯一的地址和一个专门用于发送消息的特定端口,大多数情况下这个端口是587。在后续使用Django发送邮件时,我们将看到端口的重要性。

由于我们将使用Gmail,因此与之交互的地址是smtp.gmail.com,对应的端口为587。

现在,让我们看看如何使用Django发送邮件。

创建Django项目

每个Django项目都应有一个虚拟环境,以避免混淆项目依赖。创建虚拟环境的命令如下:

python -m venv .venv

注意:如果你对虚拟环境不熟悉,请务必查阅我们的Python虚拟环境指南

上述命令将创建一个名为.venv的虚拟环境。要激活此虚拟环境,可以使用以下命令:

source .venv/bin/activate

由于Django是第三方包,你需要使用pip进行安装:

pip install django

这将安装Django的最新版本,你可以通过运行pip freeze来检查已安装的版本。

要创建Django项目,你需要调用命令行工具django-admin

django-admin startproject EmailProject

使用上述命令,你将创建一个名为EmailProject的Django项目,但你可以根据自己的需要命名项目。

接下来,进入项目目录并启动服务器:

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

# 文件底部
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 服务器主机:

Email provider SMTP server host
Gmail smtp.gmail.com
Outlook/Hotmail smtp-mail.outlook.com
Yahoo smtp.mail.yahoo.com

目前我们对此设置留空,因为稍后我们将使用 `.env` 文件来避免硬编码敏感密钥或站点特定配置。切勿直接在代码中设置凭证。

我们将使用 Django Environ 来解决这个问题。

电子邮件端口

`EMAIL_PORT` 设置必须设为 `587`,因为这是大多数 SMTP 服务器的默认端口,个人电子邮件服务提供商也是如此。

此端口与 TLS 加密配合使用,确保电子邮件发送的安全性。

使用 TLS 发送邮件

传输层安全(TLS)是一种安全协议,广泛用于网络中,用于加密 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. 启用双重认证,因为这是获取应用密码的必要步骤。
  4. 现在你已开启双重认证,接下来就是获取应用密码。你可以通过访问谷歌账户的安全部分,滚动至“登录谷歌”区域,点击应用密码来完成这一操作。

在跳转至应用密码页面之前,你需要重新输入你的密码(账户密码)。

进入后,点击选择应用,为该应用密码自定义一个名称,例如“Django发送邮件”,然后点击生成

A new window will show up with a sixteen-character password. Copy it, because we’ll need it to configure our Django project.

如果你使用的是其他邮件服务提供商,请确保阅读以下指南:

使用Django Environ隐藏敏感密钥

即使在开发中仅发送电子邮件,也不应将密码直接写入源代码。当与GitHub一起使用版本控制系统托管项目时,这一点变得更加重要。你不希望人们访问你的数据。

让我们看看如何通过使用Django-environ来防止这种情况。

EmailProject目录(settings.py文件所在位置)内创建一个.env文件,使用以下命令:

cd EmailProject/
ls

settings.py # 设置文件必须在此处

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

注意:确保你的虚拟环境已激活。

现在,打开位于EmailProject目录下的settings.py文件,并使用以下代码:

# EmailProject/settings.py
# 这应该位于文件的开头
import environ

env = environ.Env()
environ.Env.read_env()

# 之前的设置...
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')

# 自定义设置:用于邮件
RECIPIENT_ADDRESS = env('RECIPIENT_ADDRESS')

首先,我们在设置文件的顶部导入environ包。请记住,所有导入都应位于文件开头。

然后,我们创建一个env变量,它将包含.env文件中的所有键值对。

env('KEY')语句意味着我们正在查找该键的值。确保在继续之前已设置好.env文件,否则如果某些环境变量未设置,你将收到Django的ImproperlyConfigured错误。

请注意,RECIPIENT_ADDRESS是我们将用于发送邮件到一个我们可访问的地址的自定义设置。

别忘了.env文件包含在你的.gitignore中,如果你正在使用Git和GitHub。只需打开该文件并添加以下行即可完成此操作:

.env

1. 使用Django Shell发送电子邮件

终于到了本文精彩的部分!是时候向Django发送你的第一封邮件了。

打开终端,激活虚拟环境,并运行:

python manage.py shell

这将创建一个已预先配置好Django设置的shell。在那个全新的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.py文件中)的settings对象。
  • 最后,我们将所有必需的参数传递给send_mail函数。该函数返回发送的电子邮件数量,在此例中为1

注意我们如何使用settings对象来获取from_email(发送邮件的邮箱)和recipient_list(我们在.env中定义的RECIPIENT_ADDRESS自定义设置)。

现在,如果我检查我的收件箱——因为我将RECIPIENT_ADDRESS环境变量设置为我的电子邮件地址——我将收到Django发送的消息。

2. 使用Django构建自动化联系表单

在本节中,我们将使用Django表单和内置的send_mail函数构建一个自动化的联系表单。此外,我们将在联系表单内部创建一个自定义函数send(),以便更容易地在视图中实现它。

首先,我们来创建联系应用。进入项目根目录——即manage.py所在位置——并运行以下命令:

python manage.py startapp contact

接着,在EmailProject/settings.py文件中的INSTALLED_APPS变量内安装该应用:

# EmailProject/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    ...

    # 自定义
    'contact',
]

在继续处理contact应用之前,我们先在EmailProject/urls.py文件中配置urlpatterns。为此,导入django.urls.include函数,并将联系表单的URL包含到整个项目中。别担心,我们稍后会配置联系表单的URL:

# EmailProject/urls.py

from django.contrib import admin
from django.urls import path, include # 新导入

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('contact.urls')) # 包含联系表单的URL
]

联系表单

进入contact应用文件夹,创建一个forms.py文件。定义所有表单于forms.py文件内是一个良好的实践,尽管这不是强制性的。这就是为什么Django没有默认包含这个文件。

你可以通过以下命令创建表单文件:

cd ../contact/
# 你已经在EmailProject文件夹内
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
        """
        # 清洗后的数据
        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]
        )

这是一个庞大的类,让我们逐一分解每个部分的操作。首先,我们定义了四个发送消息所需的必填字段:

  • nameenquiryCharField,分别代表联系人的姓名和联系原因。
  • 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):
        调用自定义的send方法
        form.send()
        return super().form_valid(form)

如您所见,我们正在使用创建的ContactForm构建一个简单的FormView。我们还设置了template_namesuccess_url。稍后我们将编写HTML模板并设置URL。

通过form_valid方法,我们仅在表单的所有字段均有效时,使用ContactForm.send()方法发送电子邮件。这意味着如果用户输入了无效信息——例如未格式化的电子邮件地址——消息将不会被发送。

上述form_valid方法的实现,在基于函数的视图中相当于以下内容:

之前基于函数的contact_view...

if request.method == 'POST':
    form = ContactForm(request.POST)
    if form.is_valid():
        form.send()
        return redirect('contact:success')
else:
    form = ContactForm())

在本节结束时,我们将编写一个ContactSuccessView,向用户显示成功信息。由于我们已经导入了TemplateView类,因此只需继承它并定义template_name属性:

# contact/views.py
class ContactSuccessView(TemplateView):
    template_name = 'contact/success.html'

如有疑问,可查看GitHub仓库中的views.py文件。

联系URL

现在是创建contact应用URL模式的时候了。由于Django默认不提供urls.py文件,我们需要使用以下命令创建它(确保位于contact应用文件夹内):

pwd
# /path/to/project/EmailProject/contact
touch urls.py

打开该文件并设置app_nameurlpatterns变量:

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"),
]

我们使用path将路由及其对应视图包含到应用的URL配置中。当我们设置app_name变量为'contact'时,意味着应用的URL命名空间将如下所示:

contact:name_of_path
# 对于ContactView
contact:contact
# 对于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/[email protected]/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/[email protected]/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous">
    </script>
  </body>

</html>

如您所见,这是一个简单的HTML文件框架,包含了Bootstrap 5的链接。这使得我们无需使用CSS文件即可对联系应用进行样式化。

通过使用{% block name-of-block %}标签,我们可以设置一个占位符,供“子模板”使用。这种标签的使用使得模板继承变得简单易行。

在深入到表单之前,您需要安装Django crispy forms包,它允许我们轻松地对表单进行样式化:

pip install django-crispy-forms

再次,crispy_forms是一个Django应用,我们需要将其包含在INSTALLED_APPS列表中:

# config/settings.py

INSTALLED_APPS = [
    ...

    # 第三方应用
    'crispy_forms',

    # 自定义应用
    'contact',
]

# 指示Django crispy forms使用的前端框架
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模板标签来渲染表单,这也是为什么我们要加载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项目中处理敏感数据
  • 如何构建自动化的联系表单

更多关于Django的内容,请查看“使用Django构建照片分享应用”。

关于如何使用Django发送电子邮件的常见问题解答

我能否从Django发送电子邮件?

可以,你可以从Django应用程序发送电子邮件。Django提供了一个内置的电子邮件发送框架,使得发送电子邮件相对容易。

如何在Django中发送电子邮件?

如上文所述,首先在Django项目的settings.py文件中配置邮件设置。你需要指定SMTP服务器详情及认证凭据。配置完邮件设置后,便能从Django视图或函数中创建并发送邮件。可使用django.core.mail下的send_mail函数。

对于包含HTML内容的复杂邮件,可利用Django的模板系统创建邮件模板,这使得你能生成格式丰富的邮件。通过EmailMessage类可将HTML内容嵌入邮件中。若尚未执行,别忘了运行python manage.py migrate以创建发送邮件所需的数据库表。

如何在Django中发送Outlook邮件?

要在Django中发送Outlook邮件,可结合使用Django的邮件发送功能与Outlook的SMTP设置。在你的Django项目settings.py文件中,配置Outlook的SMTP设置,这将使Django能连接到Outlook SMTP服务器发送邮件。

如何在Django中接收邮件?

在您的Django项目的`settings.py`中,配置您的入站邮件服务器设置。通常,您需要为您的电子邮件提供商提供IMAP(Internet Message Access Protocol)服务器详细信息以及身份验证凭据。接下来,使用`imaplib`库连接到您的电子邮件服务器并检索电子邮件。您可以在Django视图或自定义管理命令中执行此操作。一旦获取了电子邮件,您就可以对其进行处理,提取信息,并在您的Django应用程序中执行任何必要的操作。这可能包括解析电子邮件内容,将相关数据存储在数据库中,或根据电子邮件的内容或发件人触发特定操作。

Source:
https://www.sitepoint.com/django-send-email/