Django SMTP邮件发送全攻略:从配置到自动化表单

本文详细讲解如何在Django项目中配置SMTP服务器发送邮件,包括Gmail应用密码设置、环境变量安全管理、异步邮件发送和自动化联系表单实现,提供完整的代码示例和最佳实践指南。

如何使用Django通过SMTP服务器发送邮件

关键要点

  • 配置SMTP设置:在settings.py文件中配置适当的邮件后端、主机、端口和安全设置(如TLS)来设置Django邮件发送
  • 使用Django Environ保护凭证:使用Django Environ通过环境变量安全管理敏感凭证,避免在源代码中硬编码凭证
  • 生成应用特定密码:使用Gmail时,启用两步验证并创建应用密码来安全验证Django邮件发送
  • 使用send_mail发送邮件:使用Django内置的send_mail函数从Django shell、视图或可重用辅助函数发送邮件
  • 实现自动化联系表单:使用Django Forms构建自动化联系表单并集成邮件发送功能
  • 测试邮件功能:使用单元测试验证邮件发送逻辑,并使用MailHog或控制台邮件后端进行安全开发测试
  • 遵循最佳实践:使用TLS加密、适当身份验证和模块化邮件功能确保安全高效的邮件传递

理解SMTP服务器和简单邮件传输协议

SMTP(简单邮件传输协议)是一组确定电子邮件如何从发件人传输到收件人的规则。SMTP服务器使用此协议发送和转发传出邮件。

SMTP服务器始终具有唯一地址和用于发送消息的特定端口,在大多数情况下为587。我们将看到端口在Django邮件发送中的相关性。

对于此示例,我们将使用Gmail的SMTP服务器:

  • 地址:smtp.gmail.com
  • 端口:587

创建Django项目

每个Django项目都应该有一个虚拟环境。要创建一个,请运行:

1
python -m venv .venv

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

1
2
3
4
5
6
7
8
# CMD
.venv\Scripts\activate

# Power Shell
.venv\Scripts\Activate.ps1

# WSL
source .venv/bin/activate

由于Django是第三方包,必须使用pip安装:

1
pip install django

要创建Django项目,调用命令行实用程序django-admin:

1
django-admin startproject EmailProject

进入项目目录并运行服务器:

1
2
cd EmailProject
python manage.py runserver

为SMTP配置Django邮件后端

邮件后端是Django发送邮件的机制。默认情况下,Django使用django.core.mail.backends.smtp.EmailBackend,允许它连接到SMTP服务器并发送邮件。

打开EmailProject/settings.py文件并在文件底部粘贴以下设置:

1
2
3
4
5
6
7
8
9
# 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服务器的后端。

邮件主机

EMAIL_HOST设置指的是你将使用的SMTP服务器域。这取决于你的电子邮件提供商。

电子邮件提供商 SMTP服务器主机
Gmail smtp.gmail.com
Outlook/Hotmail smtp-mail.outlook.com
Yahoo smtp.mail.yahoo.com

邮件端口

EMAIL_PORT设置必须设置为587,因为这是大多数SMTP服务器的默认端口。

使用TLS

传输层安全(TLS)是整个Web使用的安全协议,用于加密Web应用程序(Django)和服务器(SMTP服务器)之间的通信。

邮件主机用户

EMAIL_HOST_USER设置是你的个人电子邮件地址。

邮件主机密码

EMAIL_HOST_PASSWORD设置是你将从电子邮件帐户获取的应用密码。

使用应用密码设置Gmail SMTP服务器

由于Google已弃用"安全性较低的应用",连接Gmail帐户发送邮件的正确和安全方法是使用应用密码。应用密码仅在为Google帐户启用两步验证时可用。

步骤1:启用两步验证

  1. 转到你的Google帐户:https://myaccount.google.com/
  2. 在左侧菜单中导航到安全
  3. 滚动到"你如何登录Google"部分
  4. 单击"两步验证"并按照屏幕上的说明操作

步骤2:生成应用密码

启用两步验证后,在搜索栏中搜索"应用密码"。在那里输入应用密码的名称并单击创建。

使用Django Environ保护邮件后端凭证

即使你只是在开发中发送邮件,也不应该将密码直接写入源代码。

在EmailProject目录中创建.env文件:

1
2
cd EmailProject/
touch .env

打开.env文件并输入以下键值对:

1
2
3
4
EMAIL_HOST=smtp.gmail.com
EMAIL_HOST_USER=YourEmail@address
EMAIL_HOST_PASSWORD=YourAppPassword
RECIPIENT_ADDRESS=TheRecieverOfTheMails

安装Django-environ:

1
pip install django-environ

在settings.py文件中使用以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 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')

1. 使用Django Shell发送邮件

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

1
python manage.py shell

在shell中粘贴以下代码:

1
2
3
4
5
6
7
8
>>> 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

也可以使用一行代码而不指定参数:

1
2
>>> send_mail('A cool subject', 'A stunning message', settings.EMAIL_HOST_USER, [settings.RECIPIENT_ADDRESS])
1

异步邮件发送

在Django 4.x中,支持异步邮件发送以提高性能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import asyncio
from django.core.mail import send_mail
from django.conf import settings

async def send_async_email():
    await send_mail(
        subject="Async Email Test",
        message="This email is sent asynchronously with Django 4.x.",
        from_email=settings.EMAIL_HOST_USER,
        recipient_list=[settings.RECIPIENT_ADDRESS],
    )

# 调用异步函数
asyncio.run(send_async_email())

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

创建联系应用

1
python manage.py startapp contact

在INSTALLED_APPS变量中安装它:

1
2
3
4
5
6
7
8
9
# EmailProject/settings.py

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

    # 自定义
    'contact',
]

联系表单

创建forms.py文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# contact/forms.py
from django import forms
from django.conf import settings
from django.core.mail import send_mail

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):
        """
        返回格式化信息的方法
        :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]
        )

联系视图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# contact/views.py
from django.views.generic import FormView, TemplateView
from .forms import ContactForm
from django.urls import reverse_lazy

class ContactView(FormView):
    template_name = 'contact/contact.html'
    form_class = ContactForm
    success_url = reverse_lazy('contact:success')

    def form_valid(self, form):
        # 调用自定义发送方法
        form.send()
        return super().form_valid(form)

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

联系URL

1
2
3
4
5
6
7
8
9
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"),
]

编写模板

创建模板结构:

1
2
3
mkdir -p templates/contact/
cd templates/contact
touch base.html contact.html success.html

base.html模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!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>

安装Django crispy forms:

1
pip install django-crispy-forms

添加到INSTALLED_APPS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# config/settings.py

INSTALLED_APPS = [
    ...
    # 第三方应用
    'crispy_forms',
    # 自定义应用
    'contact',
]

# 指示django crispy forms使用的前端框架
CRISPY_TEMPLATE_PACK = 'bootstrap4'

contact.html模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{% 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 %}

success.html模板:

1
2
3
4
5
6
7
8
{% 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 %}

最佳实践

1. 可重用的邮件发送函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# contact/services.py
from django.core.mail import send_mail
from django.conf import settings

def send_contact_email(subject, message, recipient):
    send_mail(
        subject=subject,
        message=message,
        from_email=settings.EMAIL_HOST_USER,
        recipient_list=[recipient],
        fail_silently=False,
    )

2. 单元测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.test import TestCase
from django.core.mail import send_mail

class EmailTest(TestCase):
    def test_email_sending(self):
        response = send_mail(
            subject='Test Subject',
            message='Test Message',
            from_email='from@example.com',
            recipient_list=['to@example.com'],
        )
        self.assertEqual(response, 1)  # 检查是否发送了一封邮件

3. 发送富HTML邮件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings

def send_html_email():
    subject = 'HTML Email Example'
    from_email = settings.EMAIL_HOST_USER
    recipient = ['recipient@example.com']
    html_content = render_to_string('email_template.html', {'key': 'value'})

    msg = EmailMultiAlternatives(subject, body='', from_email=from_email, to=recipient)
    msg.attach_alternative(html_content, "text/html")
    msg.send()

4. 集成第三方邮件服务

1
2
3
EMAIL_BACKEND = 'sendgrid_backend.SendgridBackend'
SENDGRID_API_KEY = 'your-api-key'
SENDGRID_SANDBOX_MODE_IN_DEBUG = False

5. 用户注册中的邮件验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from django.core.mail import send_mail
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
from django.template.loader import render_to_string

def send_verification_email(user, request):
    token = account_activation_token.make_token(user)
    uid = urlsafe_base64_encode(force_bytes(user.pk))
    link = request.build_absolute_uri(f'/activate/{uid}/{token}/')
    message = render_to_string('activation_email.html', {'link': link})
    send_mail('Verify your email', message, 'from@example.com', [user.email])

总结

恭喜!你已经学会了如何使用Django发送邮件以及如何构建Django联系表单。

有许多方法可以使用Django发送邮件。在本教程中,你使用个人电子邮件地址完成了这一操作,但我希望你能探索其他工具并将它们集成到你的项目中。

常见问题解答

我可以用Django发送邮件吗?

是的,Django提供了一个内置的邮件发送框架,使发送邮件变得简单。通过在settings.py文件中配置SMTP设置,你可以使用send_mail函数或EmailMessage类发送邮件。

如何在Django中发送邮件?

配置Django发送邮件:

  1. 在settings.py文件中设置SMTP详细信息
  2. 如果使用Gmail或类似提供商,使用安全密码而不是主电子邮件密码
  3. 对于生产环境,考虑使用SendGrid、Mailgun或Amazon SES等第三方服务以获得更好的可扩展性

如何测试Django中的邮件发送而不发送真实邮件?

在开发期间,你可以使用可选的邮件后端来测试邮件而无需连接到真实的SMTP服务器。例如:

控制台邮件后端:将邮件消息打印到终端而不是发送它们。

1
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计