[Django 0-1] Core.Email 模块
Mail 源码分析
模块文件结构
.
├── __init__.py
├── backends
│ ├── __init__.py
│ ├── base.py
│ ├── console.py
│ ├── dummy.py
│ ├── filebased.py
│ ├── locmem.py
│ └── smtp.py
├── message.py
└── utils.py
模块结构
backends
: 邮件后端,包括 console,dummy,filebased,localmem,smtpmessage
: 主要是消息的抽象,分为 plaintext,html 两类
ConsoleEmailBackend 类
属性
stream
: 输出流
方法
send_messages(email_messages)
: 发送邮件
def write_message(self, message):
msg = message.message()
msg_data = msg.as_bytes()
charset = (
msg.get_charset().get_output_charset() if msg.get_charset() else "utf-8"
)
msg_data = msg_data.decode(charset)
self.stream.write("%s\n" % msg_data)
self.stream.write("-" * 79)
self.stream.write("\n")
def send_messages(self, email_messages):
"""Write all messages to the stream in a thread-safe way."""
if not email_messages:
return
msg_count = 0
with self._lock:
try:
stream_created = self.open()
for message in email_messages:
self.write_message(message)
self.stream.flush() # flush after each message
msg_count += 1
if stream_created:
self.close()
except Exception:
if not self.fail_silently:
raise
return msg_count
DummyEmailBackend 类
可以用于本地测试,验证方法调用是否成功
方法
send_messages(email_messages)
: 发送邮件
def send_messages(self, email_messages):
return len(list(email_messages))
FileBasedEmailBackend 类
文件流,将邮件消息写入文件流中
方法
send_messages(email_messages)
: 发送邮件
def write_message(self, message):
self.stream.write(message.message().as_bytes() + b"\n")
self.stream.write(b"-" * 79)
self.stream.write(b"\n")
LocmemEmailBackend 类
内存中存储邮件消息,可以用于测试或开发
方法
send_messages(email_messages)
: 发送邮件
def send_messages(self, messages):
"""Redirect messages to the dummy outbox"""
msg_count = 0
for message in messages: # .message() triggers header validation
message.message()
mail.outbox.append(copy.deepcopy(message))
msg_count += 1
return msg_count
SMTPEmailBackend 类
SMTP 邮件后端,通过 SMTP 协议发送邮件
方法
send_messages(email_messages)
: 发送邮件
def send_messages(self, email_messages):
"""
Send one or more EmailMessage objects and return the number of email
messages sent.
"""
if not email_messages:
return 0
with self._lock:
new_conn_created = self.open()
if not self.connection or new_conn_created is None:
# We failed silently on open().
# Trying to send would be pointless.
return 0
num_sent = 0
try:
for message in email_messages:
sent = self._send(message)
if sent:
num_sent += 1
finally:
if new_conn_created:
self.close()
return num_sent
EmailMessage 类
属性
subject
:邮件主题body
:邮件正文from_email
:发件人地址to
:收件人地址列表cc
:抄送人地址列表bcc
: 密送人地址列表reply_to
:回复地址列表headers
:自定义邮件头attachments
:附件列表connection
: 邮件服务器
方法
- attach(): 添加附件
邮件附件如果已经是 mime.MIMEBase 的话,直接加入到附件列表中。
如果不是,先猜测 mimetype 类型,然后以(filename, content, mimetype)加入到附件列表中。
filename=None, mimetype=None 的情况,会出错。
def attach(self, filename=None, content=None, mimetype=None):
"""
Attach a file with the given filename and content. The filename can
be omitted and the mimetype is guessed, if not provided.
If the first parameter is a MIMEBase subclass, insert it directly
into the resulting message attachments.
For a text/* mimetype (guessed or specified), when a bytes object is
specified as content, decode it as UTF-8. If that fails, set the
mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content.
"""
if isinstance(filename, MIMEBase):
if content is not None or mimetype is not None:
raise ValueError(
"content and mimetype must not be given when a MIMEBase "
"instance is provided."
)
self.attachments.append(filename)
elif content is None:
raise ValueError("content must be provided.")
else:
mimetype = (
mimetype
or mimetypes.guess_type(filename)[0]
or DEFAULT_ATTACHMENT_MIME_TYPE
)
basetype, subtype = mimetype.split("/", 1)
if basetype == "text":
if isinstance(content, bytes):
try:
content = content.decode()
except UnicodeDecodeError:
# If mimetype suggests the file is text but it's
# actually binary, read() raises a UnicodeDecodeError.
mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
self.attachments.append((filename, content, mimetype))
- message(): 生成 MIMEText 对象
def message(self):
encoding = self.encoding or settings.DEFAULT_CHARSET
msg = SafeMIMEText(self.body, self.content_subtype, encoding)
msg = self._create_message(msg)
msg["Subject"] = self.subject
msg["From"] = self.extra_headers.get("From", self.from_email)
self._set_list_header_if_not_empty(msg, "To", self.to)
self._set_list_header_if_not_empty(msg, "Cc", self.cc)
self._set_list_header_if_not_empty(msg, "Reply-To", self.reply_to)
# Email header names are case-insensitive (RFC 2045), so we have to
# accommodate that when doing comparisons.
header_names = [key.lower() for key in self.extra_headers]
if "date" not in header_names:
# formatdate() uses stdlib methods to format the date, which use
# the stdlib/OS concept of a timezone, however, Django sets the
# TZ environment variable based on the TIME_ZONE setting which
# will get picked up by formatdate().
msg["Date"] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME)
if "message-id" not in header_names:
# Use cached DNS_NAME for performance
msg["Message-ID"] = make_msgid(domain=DNS_NAME)
for name, value in self.extra_headers.items():
if name.lower() != "from": # From is already handled
msg[name] = value
return msg
EmailMultiAlternatives 类
EmailMultiAlternatives 类继承自 EmailMessage 类,增加了对 HTML 格式的支持。
总结
Django 的邮件模块提供了多种邮件后端,可以根据需要选择不同的后端。留有 dummy,console,locmem,filebased,smtp 多种类型后端。
通过 Django 邮件模块,避免重复编码,针对各类邮件服务器的特定规则只需自己实现发送后端逻辑,不需要重头编写。
原文地址:https://blog.csdn.net/qq_35104586/article/details/136802973
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!