Django筆記三十八之傳送郵件

XHunter發表於2023-05-08

本文首發於公眾號:Hunter後端

原文連結:Django筆記三十八之傳送郵件

這一篇筆記介紹如何在 Django 中傳送郵件。

在 Python 中,提供了 smtplib 的郵件模組,而 Django 在這個基礎上對其進行了封裝,我們可以透過 django.core.mail 來呼叫。

以下是本篇筆記的目錄:

  1. 郵件配置項
  2. send_mail
  3. EmailMessage
  4. 複用郵件傳送連線
  5. 開發階段除錯設定

1、郵件配置項

在正式傳送郵件前,我們需要在 settings.py 裡設定幾個引數,如下:

# 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 日誌記錄那一篇筆記中有過介紹,那是我們指定日誌等級傳送郵件的功能,這裡再做一下簡單的介紹。

EMAIL_BACKEND 是我們指定的郵箱後端,在後面我們會介紹在開發除錯階段的時候可以設定的其他值

EMAIL_HOST 傳送郵箱的主機地址,這裡我們使用的是 163 郵箱的地址

EMAIL_PORT EMAIL_HOST 使用的埠

EMAIL_HOST_USER 發件人郵箱地址

EMAIL_HOST_PASSWORD 163 郵箱開啟了 SMTP 服務提供的授權碼

EMAIL_USE_SSL 與 SMTP 伺服器對話時是否使用隱式 TLS 連線,這種型別被稱為 SSL,通常在 465 埠使用,這個欄位與 EMAIL_USE_TLS 是互相排斥的,只能設定一個為 True

EMAIL_USE_TLS 與 SMTP 伺服器對話是否使用 TLS 連線,一般在 587 埠

以上就是在 Django 裡使用 163 郵箱的一個配置項示例。

2、send_mail

配置好之後我們就可以嘗試傳送一下郵件,最簡單的使用示例如下:

from django.core.mail import send_mail

send_mail(
    subject="subject 主題",
    message="郵件主體",
    from_email="hunterxx@163.com",
    recipient_list=["120460xxxx@qq.com"],
)

在上面的呼叫中,subject 是傳送的郵件的標題,

message 是郵件傳送的正文內容。

from_email 是傳送郵件的郵箱

recipient_list 是接收收件人列表,可以接收多個郵箱地址

對於 message 引數,接收的是純文字資訊,會將引數內容直接顯示在郵件正文,如果是想對文字進行更多操作,比如加大字型,加粗,或者加上表格等操作,可以使用 html_message 引數來替代 message 引數。

比如:

send_mail(
    subject="subject 主題",
    from_email="hunterxx@163.com",
    recipient_list=["120460xxxx@qq.com"],
    html_message="<h1>html main body</h1>"
)

在這裡,html_message 將引數內容當作一個 html 文字進行解析,傳送郵件後就可以在接收郵箱看到大號的文字字型了。

傳送批次郵件

如果有批次傳送郵件的需求,可以使用 send_mass_mail 方法。

from django.core.mail import send_mass_mail

message_1 = ("郵件標題1", "郵件正文1", "hunterxxx@163.com", ["120460xxxx@qq.com"])
message_2 = ("郵件標題2", "郵件正文2", "hunterxxx4@163.com", ["120460xxx6@qq.com"])
send_mass_mail(
    (message_1, message_2)

該方法接收列表引數,其中列表的每一個元素的引數和引數順序都是固定的,分別是郵件標題,正文,郵件傳送人,和郵件接收人列表。

注意: 因為批次傳送的引數是固定的,所以並不支援 send_mail 裡的 html_message 引數。

3、EmailMessage

前面介紹的 send_mail() 方法簡單可用,但是並不支援郵件裡的附件、抄送等功能,接下來我們使用 EmailMessage 這個類來實現這些額外的功能。

以下是使用 EmailMessage 實現傳送郵件的簡單示例:

from django.core.mail import EmailMessage

email = EmailMessage(
    subject="郵件標題",
    body="郵件主體",
    from_email="hunterxxx@163.com",
    to=["120460xxx@qq.com"],
)
email.send()

引數名稱與 send_mail() 略有不同,這裡的郵件正文是 body,接收人列表為 to。

這裡在例項化 EmailMessage 之後,呼叫 send() 方法即可傳送郵件。

除了上面的這些引數,還有 bcc,實現的是密送功能,也是郵件接收人列表,cc 是抄送人列表。

還有 attachments 引數,實現的是附件功能,接下來介紹幾種傳送附件的方式:

傳送附件

1. attachments 引數

我們可以直接在 EmailMessage() 中新增附件引數,attachments 引數接收一個列表,列表元素也是一個列表,內層的這個列表接收三個元素,第一個元素為檔名,第二個元素為檔案內容,第三個元素為指定的附件的 MIME 型別,第三個引數省略的話就會參考附件的檔名自動選擇。

我們在系統根目錄下建立兩個檔案 a.txt, b.txt,然後實現示例如下:

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="郵件標題",
    body="郵件主體",
    from_email="hunterxxxx@163.com",
    to=["120460xxxx@qq.com"],
    attachments=attachments,
)
email.send()
2. attach() 方法

除了直接在 EmailMessage 例項中新增引數,我們還可以使用 attach() 方法。

示例如下:

email = EmailMessage(
    subject="郵件標題",
    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() 方法,引數內容是檔案路徑+檔名,系統會自動為我們解析該檔案:

email = EmailMessage(
    subject="郵件標題",
    body="郵件主體",
    from_email="hunterxxxx@163.com",
    to=["120460xxx@qq.com"],
)

email.attach_file("./b.txt")
email.send()

EmailMessage 傳送 html 正文

前面介紹了在 send_mail() 方法可以透過 html_message 的引數傳送 html 頁面的郵件,在 EmailMessage 也可以實現,但是需要修改 content_subtype 屬性。

預設情況下,EmailMessage.content_subtype 是 "plain",我們將其改為 "html" 即可傳送 html 頁面的郵件。

email = EmailMessage(
    subject="郵件標題",
    body="<h1>郵件主體</h1>",
    from_email="hunterxxxx@163.com",
    to=["120460xxx@qq.com"],
)
email.content_subtype = "html"
email.send()

4、複用郵件傳送連線

因為傳送郵件涉及到網路連線及可能存在的大量資料的傳送,比如附件。

所以,如果是在介面中有傳送郵件的需求,我們可以透過 celery 的非同步任務實現傳送郵件的功能。

而郵件的傳送會涉及到 SMTP 連線的建立和關閉,所以複用連線也是一個好的方式。

這裡介紹兩種方式:

send_messages

send_messages() 方法接收 EmailMessage 例項列表,然後實現批次傳送的功能:

from django.core import mail
from django.core.mail import EmailMessage

email_1 = EmailMessage(
    subject="郵件標題1",
    body="郵件主體1",
    from_email="hunterxxxx@163.com",
    to=["120460xxxx@qq.com"],
)
email_2 = EmailMessage(
    subject="郵件標題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 的建立和關閉。

from django.core import mail

connection = mail.get_connection()

email_1 = mail.EmailMessage(
    subject="郵件標題1",
    body="郵件主體1",
    from_email="hunterxxx@163.com",
    to=["120460xxx@qq.com"],
    connection=connection
)
email_1.send()


email_2 = mail.EmailMessage(
    subject="郵件標題2",
    body="郵件主體2",
    from_email="hunterxxxx@163.com",
    to=["120460xxxx@qq.com"],
)


email_3 = mail.EmailMessage(
    subject="郵件標題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 的呼叫增加了 connection 引數,email_2 和 email_3 也是使用 connection 進行的批次傳送

這個過程中,connection 一直沒有關閉,所以複用的是同一個連線,直到最後呼叫 close() 才算是手動關閉了這個 connection 連線。

5、開發階段除錯設定

在開發階段,我們除錯傳送郵件功能的時候,有時候並不想每次都真的傳送郵件給指定賬戶,儘管可能是測試賬號,我們有時候只想看一下輸出的內容,可以更改郵箱配置的後端

console

我們可以在 settings.py 裡設定:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

這樣,在呼叫我們前面的 send 方法後,系統就不會傳送郵件給 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

在除錯階段,我們還可以指定將郵件的內容輸出到檔案,同樣的修改郵件後端配置:

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = './emails_file'

這裡設定了郵件後端為檔案,EMAIL_FILE_PATH 則是指定了郵件內容放到系統根目錄下的 emails_file 檔案中。

呼叫了傳送郵件的函式後,在這個資料夾下就會多出一個檔案,檔案內容是我們前面在 console 控制檯輸出的內容

如果想獲取更多後端相關文章,可掃碼關注閱讀:
image