[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,smtp
  • message: 主要是消息的抽象,分为 plaintext,html 两类

ConsoleEmailBackend 类

属性
  1. stream: 输出流
方法
  1. 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 类

可以用于本地测试,验证方法调用是否成功

方法
  1. send_messages(email_messages): 发送邮件
def send_messages(self, email_messages):
    return len(list(email_messages))

FileBasedEmailBackend 类

文件流,将邮件消息写入文件流中

方法
  1. 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 类

内存中存储邮件消息,可以用于测试或开发

方法
  1. 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 协议发送邮件

方法
  1. 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 类

属性
  1. subject:邮件主题
  2. body:邮件正文
  3. from_email:发件人地址
  4. to:收件人地址列表
  5. cc:抄送人地址列表
  6. bcc: 密送人地址列表
  7. reply_to:回复地址列表
  8. headers:自定义邮件头
  9. attachments:附件列表
  10. connection: 邮件服务器
方法
  1. 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))
  1. 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 邮件模块,避免重复编码,针对各类邮件服务器的特定规则只需自己实现发送后端逻辑,不需要重头编写。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值