這一篇筆記介紹如何在 Django 中發(fā)送郵件。
在 Python 中,提供了 smtplib 的郵件模塊,而 Django 在這個基礎(chǔ)上對其進(jìn)行了封裝,我們可以通過 django.core.mail 來調(diào)用。
以下是本篇筆記的目錄:
- 郵件配置項
- send_mail
- EmailMessage
- 復(fù)用郵件發(fā)送連接
- 開發(fā)階段調(diào)試設(shè)置
1、郵件配置項
在正式發(fā)送郵件前,我們需要在 settings.py 里設(shè)置幾個參數(shù),如下:
# hunter/settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = 'hunterxxxx04@163.com'
EMAIL_HOST_PASSWORD = 'JBDMVIXSHYxxxxx'
EMAIL_USE_SSL = True
EMAIL_USE_TLS = False
這些配置項在 log 日志記錄那一篇筆記中有過介紹,那是我們指定日志等級發(fā)送郵件的功能,這里再做一下簡單的介紹。
EMAIL_BACKEND 是我們指定的郵箱后端,在后面我們會介紹在開發(fā)調(diào)試階段的時候可以設(shè)置的其他值
EMAIL_HOST 發(fā)送郵箱的主機(jī)地址,這里我們使用的是 163 郵箱的地址
EMAIL_PORT EMAIL_HOST 使用的端口
EMAIL_HOST_USER 發(fā)件人郵箱地址
EMAIL_HOST_PASSWORD 163 郵箱開啟了 SMTP 服務(wù)提供的授權(quán)碼
EMAIL_USE_SSL 與 SMTP 服務(wù)器對話時是否使用隱式 TLS 連接,這種類型被稱為 SSL,通常在 465 端口使用,這個字段與 EMAIL_USE_TLS 是互相排斥的,只能設(shè)置一個為 True
EMAIL_USE_TLS 與 SMTP 服務(wù)器對話是否使用 TLS 連接,一般在 587 端口
以上就是在 Django 里使用 163 郵箱的一個配置項示例。
2、send_mail
配置好之后我們就可以嘗試發(fā)送一下郵件,最簡單的使用示例如下:
from django.core.mail import send_mail
send_mail(
subject="subject 主題",
message="郵件主體",
from_email="hunterxx@163.com",
recipient_list=["120460xxxx@qq.com"],
)
在上面的調(diào)用中,subject 是發(fā)送的郵件的標(biāo)題,
message 是郵件發(fā)送的正文內(nèi)容。
from_email 是發(fā)送郵件的郵箱
recipient_list 是接收收件人列表,可以接收多個郵箱地址
對于 message 參數(shù),接收的是純文本信息,會將參數(shù)內(nèi)容直接顯示在郵件正文,如果是想對文本進(jìn)行更多操作,比如加大字體,加粗,或者加上表格等操作,可以使用 html_message 參數(shù)來替代 message 參數(shù)。
比如:
send_mail(
subject="subject 主題",
from_email="hunterxx@163.com",
recipient_list=["120460xxxx@qq.com"],
html_message="<h1>html main body</h1>"
)
在這里,html_message 將參數(shù)內(nèi)容當(dāng)作一個 html 文本進(jìn)行解析,發(fā)送郵件后就可以在接收郵箱看到大號的文本字體了。
發(fā)送批量郵件
如果有批量發(fā)送郵件的需求,可以使用 send_mass_mail 方法。
from django.core.mail import send_mass_mail
message_1 = ("郵件標(biāo)題1", "郵件正文1", "hunterxxx@163.com", ["120460xxxx@qq.com"])
message_2 = ("郵件標(biāo)題2", "郵件正文2", "hunterxxx4@163.com", ["120460xxx6@qq.com"])
send_mass_mail(
(message_1, message_2)
該方法接收列表參數(shù),其中列表的每一個元素的參數(shù)和參數(shù)順序都是固定的,分別是郵件標(biāo)題,正文,郵件發(fā)送人,和郵件接收人列表。
注意: 因為批量發(fā)送的參數(shù)是固定的,所以并不支持 send_mail 里的 html_message 參數(shù)。
3、EmailMessage
前面介紹的 send_mail() 方法簡單可用,但是并不支持郵件里的附件、抄送等功能,接下來我們使用 EmailMessage 這個類來實現(xiàn)這些額外的功能。
以下是使用 EmailMessage 實現(xiàn)發(fā)送郵件的簡單示例:
from django.core.mail import EmailMessage
email = EmailMessage(
subject="郵件標(biāo)題",
body="郵件主體",
from_email="hunterxxx@163.com",
to=["120460xxx@qq.com"],
)
email.send()
參數(shù)名稱與 send_mail() 略有不同,這里的郵件正文是 body,接收人列表為 to。
這里在實例化 EmailMessage 之后,調(diào)用 send() 方法即可發(fā)送郵件。
除了上面的這些參數(shù),還有 bcc,實現(xiàn)的是密送功能,也是郵件接收人列表,cc 是抄送人列表。
還有 attachments 參數(shù),實現(xiàn)的是附件功能,接下來介紹幾種發(fā)送附件的方式:
發(fā)送附件
1. attachments 參數(shù)
我們可以直接在 EmailMessage() 中添加附件參數(shù),attachments 參數(shù)接收一個列表,列表元素也是一個列表,內(nèi)層的這個列表接收三個元素,第一個元素為文件名,第二個元素為文件內(nèi)容,第三個元素為指定的附件的 MIME 類型,第三個參數(shù)省略的話就會參考附件的文件名自動選擇。
我們在系統(tǒng)根目錄下創(chuàng)建兩個文件 a.txt, b.txt,然后實現(xiàn)示例如下:
from django.core.mail import EmailMessage
attachments = []
for file_name in ["./a.txt", "./b.txt"]:
with open(file_name, "r") as f:
content = f.read()
attachments.append((file_name, content))
email = EmailMessage(
subject="郵件標(biāo)題",
body="郵件主體",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
attachments=attachments,
)
email.send()
2. attach() 方法
除了直接在 EmailMessage 實例中添加參數(shù),我們還可以使用 attach() 方法。
示例如下:
email = EmailMessage(
subject="郵件標(biāo)題",
body="郵件主體",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
)
file_name_1 = "./a.txt"
f = open(file_name_1, "r")
file_content_1 = f.read()
f.close()
email.attach(file_name_1, file_content_1)
email.send()
3. attach_file() 方法
還有一個方式是使用 attach_file() 方法,參數(shù)內(nèi)容是文件路徑+文件名,系統(tǒng)會自動為我們解析該文件:
email = EmailMessage(
subject="郵件標(biāo)題",
body="郵件主體",
from_email="hunterxxxx@163.com",
to=["120460xxx@qq.com"],
)
email.attach_file("./b.txt")
email.send()
EmailMessage 發(fā)送 html 正文
前面介紹了在 send_mail() 方法可以通過 html_message 的參數(shù)發(fā)送 html 頁面的郵件,在 EmailMessage 也可以實現(xiàn),但是需要修改 content_subtype 屬性。
默認(rèn)情況下,EmailMessage.content_subtype 是 "plain",我們將其改為 "html" 即可發(fā)送 html 頁面的郵件。
email = EmailMessage(
subject="郵件標(biāo)題",
body="<h1>郵件主體</h1>",
from_email="hunterxxxx@163.com",
to=["120460xxx@qq.com"],
)
email.content_subtype = "html"
email.send()
4、復(fù)用郵件發(fā)送連接
因為發(fā)送郵件涉及到網(wǎng)絡(luò)連接及可能存在的大量數(shù)據(jù)的傳送,比如附件。
所以,如果是在接口中有發(fā)送郵件的需求,我們可以通過 celery 的異步任務(wù)實現(xiàn)發(fā)送郵件的功能。
而郵件的發(fā)送會涉及到 SMTP 連接的創(chuàng)建和關(guān)閉,所以復(fù)用連接也是一個好的方式。
這里介紹兩種方式:
send_messages
send_messages() 方法接收 EmailMessage 實例列表,然后實現(xiàn)批量發(fā)送的功能:
from django.core import mail
from django.core.mail import EmailMessage
email_1 = EmailMessage(
subject="郵件標(biāo)題1",
body="郵件主體1",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
)
email_2 = EmailMessage(
subject="郵件標(biāo)題2",
body="郵件主體2",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
)
connection = mail.get_connection()
messages = [email_1, email_2]
connection.send_messages(messages)
手動控制 connection
我們可以手動控制 connection 的創(chuàng)建和關(guān)閉。
from django.core import mail
connection = mail.get_connection()
email_1 = mail.EmailMessage(
subject="郵件標(biāo)題1",
body="郵件主體1",
from_email="hunterxxx@163.com",
to=["120460xxx@qq.com"],
connection=connection
)
email_1.send()
email_2 = mail.EmailMessage(
subject="郵件標(biāo)題2",
body="郵件主體2",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
)
email_3 = mail.EmailMessage(
subject="郵件標(biāo)題3",
body="郵件主體3",
from_email="hunterxxxx@163.com",
to=["120460xxxx@qq.com"],
)
messages = [email_2, email_3]
connection.send_messages(messages)
connection.close()
在這里,email_1 的調(diào)用增加了 connection 參數(shù),email_2 和 email_3 也是使用 connection 進(jìn)行的批量發(fā)送
這個過程中,connection 一直沒有關(guān)閉,所以復(fù)用的是同一個連接,直到最后調(diào)用 close() 才算是手動關(guān)閉了這個 connection 連接。
5、開發(fā)階段調(diào)試設(shè)置
在開發(fā)階段,我們調(diào)試發(fā)送郵件功能的時候,有時候并不想每次都真的發(fā)送郵件給指定賬戶,盡管可能是測試賬號,我們有時候只想看一下輸出的內(nèi)容,可以更改郵箱配置的后端
console
我們可以在 settings.py 里設(shè)置:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
這樣,在調(diào)用我們前面的 send 方法后,系統(tǒng)就不會發(fā)送郵件給 to 的接收人列表了,而是會在控制臺輸出我們的郵件信息:
類似如下:
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject: =?utf-8?b?6YKu5L2qCH6aKY?=
From: hunterxiong04@163.com
To: 120460xxxx@qq.com
Date: Fri, 17 Feb 2023 18:01:21 -0000
Message-ID:
<167665688132.1114.884170460108140763@1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa>
<h1>郵件主體</h1>
-------------------------------------------------------------------------------
filebased
在調(diào)試階段,我們還可以指定將郵件的內(nèi)容輸出到文件,同樣的修改郵件后端配置:
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = './emails_file'
這里設(shè)置了郵件后端為文件,EMAIL_FILE_PATH 則是指定了郵件內(nèi)容放到系統(tǒng)根目錄下的 emails_file 文件中。
調(diào)用了發(fā)送郵件的函數(shù)后,在這個文件夾下就會多出一個文件,文件內(nèi)容是我們前面在 console 控制臺輸出的內(nèi)容
原文鏈接:Django筆記三十八之發(fā)送郵件