Django学习8-邮件发送、Celery异步任务、消息

Django邮件

在用户注册时使用了邮件,现在修改注册验证机制。注册时,系统发送验证邮件到提交的邮件地址,验证后,注册才算成功。通过邮箱可以进行修改密码操作。

配置相关参数

根据官网的设置,使用Django需要在 setting.py 中进行一些设置。Django默认使用SMTP传输协议,为了使用其后台,添加:

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

同时要设置的参数:

  • EMAIL_HOST:使用的SMTP邮件服务器地址;
  • EMAIL_PORT:服务器的端口;
  • EMAIL_HOST_USER:邮箱用户名;
  • EMAIL_HOST_PASSWORD:邮箱用户密码;
  • EMAIL_USE_TLS:是否启用TLS协议;
  • EMAIL_USE_SSLSSL协议,使用的端口为465(与EMAIL_USE_TLS互斥);
  • EMAIL_TIMEOUT:指定阻塞操作的超时时间(秒)。
  • EMAIL_SSL_KEYFILE:启用SSl时,可指定用于ssl链接的私钥文件
  • EMAIL_SSL_CERTFILE:启用SSl时,可指定用于ssl链接的证书文件

发送邮件

send_mail()

Django 提供了send_mail()函数,可以轻松的进行邮件发送。
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)

  • subject:邮件的主题;
  • message:邮件内容;
  • from_email:邮件发送人"xxx@xx"
  • recipient_list:邮件接收者[email_address1, email_address2,…]
  • fail_silently:设置为False时,发送邮件出错时会报错;
  • auth_userauth_password:SMTP验证需要的用户名和密码,没有设置时使用setting中设置的值;
  • connection:指定一个邮件发送连接email-sending connection,没有设置时会自动创建一个;
  • html_message:html格式的内容;

修改注册视图函数在注册时发送验证邮件:

            authenticated_user = authenticate(username=new_user.username,
                                              password=request.POST.get('password1', ""))
            send_mail('注册', '验证信息', EMAIL_HOST_USER, [new_user.email,], fail_silently=False)
            login(request, authenticated_user)
            return HttpResponseRedirect(reverse('learning_logs:index'))

查看邮件信息
在这里插入图片描述

send_mass_mail()

send_mass_mail()与send_mail()类似,但它可以一次性发送出多封邮件,使用方法:

message1 = ('Subject here', 'Here is the message', 'from@example.com', ['first@example.com', 'other@example.com'])
message2 = ('Another Subject', 'Here is another message', 'from@example.com', ['second@test.com'])
send_mass_mail((message1, message2), fail_silently=False)

两者区别:send_mail()在每次执行时都会建立一个邮件发送连接(email-sending connection);而send_mass_mail在发送多封邮件时会使用一个连接,这使得它在发送多封邮件时比send_mail更有效率。

自定义异步任务发送邮件

在上述注册函数中邮件发送是在主进程中,点击注册后会等待一段时间。在用户邮箱注册的时候, 在发送邮件的时候可以先把"已经发送激活邮件到邮箱"返回给用户, 同时把邮件发送任务提交到异步处理线程中。
myemail.py

from django.core.mail import send_mail as core_send_mail
from django.core.mail import EmailMultiAlternatives
import threading

class MyEmailThread(threading.Thread):
    """多线程,发送邮件"""
    def __init__(self, subject, body, from_email, recipient_list, fail_silently, html_message):
        threading.Thread.__init__(self)
        self.subject = subject
        self.body = body
        self.recipient_list = recipient_list
        self.from_email = from_email
        self.fail_silently = fail_silently
        self.html_message = html_message

    # 发送邮件
    def run(self):
        mail = EmailMultiAlternatives(self.subject, self.body, self.from_email, self.recipient_list)
        if self.html_message :
            mail.attach_alternative(self.html_message, 'text/html')
        return mail.send(self.fail_silently)

# 创建线程 start 启动线程活动,会调用run方法
def my_send_mail(subject, body, from_email, recipient_list, fail_silently=False, html_message=None, *args, **kwargs):
    MyEmailThread(subject, body, from_email, recipient_list, fail_silently, html_message).start()

根据python多线程send_email的源码,将发送邮件的任务放在一个新的线程中进行。
调用自定义的邮件发送:

from .my_email import my_send_mail
# ...
my_send_mail('注册', '验证',EMAIL_HOST_USER, [new_user.email])

在这里插入图片描述点击注册后立即跳转到主页,邮件接受的速度也大幅提升。

Celery异步任务框架发送邮件

使用celery异步任务处理框架,进行异步邮件发送,详细见Celery学习
安装djcelery:pip install django-celery,安装并使用Redis作为中间件broker和结果后端backend:pip install redis

在setting下加入

import djcelery
djcelery.setup_loader()

设置Celery的加载路径,并添加djceleryINSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 创建的应用
    'learning_logs',
    'users',
    'djcelery',
    'bootstrap4',
]

同样的Celery的一些参数配置,可以放到setting下而不用单独的配置文件:

# Celery设置
# 时区
CELERY_TIMEZONE = 'Asia/Shanghai'
# 中间件
BROKER_URL= 'redis://password@localhost:6379/0'

CELERY_RESULT_BACKEND = 'redis://password@localhost:6379/0'

在需要异步任务的应用下添加tasks.py,内部是要执行的异步任务

from django.core.mail import send_mail,send_mass_mail, EmailMessage
from celery import task
import os


@task
def send_register_email(message, to):
    send_mail('注册', message,
              os.environ.get('EMAIL_HOST_USER'),
              [to, ], fail_silently=False)

在需要调用异步任务时,通过.delay()apply_async)方法让发送邮件的任务异步执行

from users.tasks import send_register_email
...
            authenticated_user = authenticate(username=new_user.username,
                                              password=request.POST.get('password1', ""))
            send_register_email.delay("注册",new_user.email)
            login(request, authenticated_user)

在运行时先运行项目python manage.py runserver,再运行Celerypython manage.py celery worker -l info 启动celery的任务执行单元worker

 -------------- celery@ulysses v3.1.26.post2 (Cipater)
---- **** ----- 
--- * ***  * -- Linux-4.15.0-36-generic-x86_64-with-Ubuntu-18.04-bionic
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         default:0x7fec39c32208 (djcelery.loaders.DjangoLoader)
- ** ---------- .> transport:   redis://localhost:6379/0
- ** ---------- .> results:     redis://localhost:6379/0
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- 
--- ***** ----- [queues]
 -------------- .> celery           exchange=celery(direct) key=celery
                

[tasks]
  . users.tasks.send_register_email

[2018-10-29 10:32:47,752: INFO/MainProcess] Connected to redis://localhost:6379/0
[2018-10-29 10:32:47,760: INFO/MainProcess] mingle: searching for neighbors
[2018-10-29 10:32:48,773: INFO/MainProcess] mingle: all alone

这次注册完成后,直接进入了主界面,在worker后台可以看到邮件发送任务在异步执行

[2018-10-29 10:46:17,772: INFO/MainProcess] Received task: users.tasks.send_register_email[39e827a5-13dd-4efd-beba-6612061fde2f]
[2018-10-29 10:46:30,005: INFO/MainProcess] Task users.tasks.send_register_email[39e827a5-13dd-4efd-beba-6612061fde2f] succeeded in 12.232193824000205s: None

上面使用Redis作为CELERY_RESULT_BACKEND,也可以使用Django ORM/Cache作为任务结果
设置结果后端CELERY_RESULT_BACKEND = 'django-db'CELERY_RESULT_BACKEND = 'django-cache'
celery数据迁移:python manage.py migrate djcelery

HTML格式邮件

在邮件的正文中添加html格式的文本并添加附件。

@task
def send_register_email(user):

    subject, from_email, to = "注册", os.environ.get('EMAIL_HOST_USER'), \
                              user.email
    text_content = "{{ user.username }},你好\
                    欢迎来到 Ulysses\
                    为了验证您的账户,请点击以下链接进行验证\
                    链接\
                    Ulysses\
                    请勿回复此邮件"
    html_content = f"<p>{user.username}, 你好</p>\
                    <p>欢迎来到 <b>Ulysses</b>!</p>\
                    <p>为了验证您的账户,请点击进行验证</p>\
                    <p>或者您可以在浏览器被输入以下内容:</p>\
                    <p>链接</p>\
                    <p>Ulysses</p>\
                    <p><small>请勿回复此邮件</small></p>"
    email = EmailMultiAlternatives(subject=subject, body=text_content, from_email=from_email, to=[to])
    email.attach_alternative(html_content, 'text/html')
    # email.attach_alternative()
    # 添加附件
    email.attach_file('users/templates/email/confirm.html', 'text/plain')
    email.attach_file('users/templates/email/confirm.txt', 'text/html')
    email.send()

EmailMultiAlternatives类是EmailMessage的一个子类(send_mail就是对EmailMessage的简单包装), 通过attach_alternative方法可以在邮件正文中添加其他类型的内容,代替原有的text/plain类型body。若邮箱不支持接受html格式文本,就会显示默认text/plain格式文本。attach_file方法可以添加邮件附件,若为MIME类型需要指明。
在这里插入图片描述

Django消息

在Web应用程序中,经常需要在处理表单或其他类型的用户输入后向用户显示一次性通知消息(也称为“flash message”)。Django提供了支持cookie和会话(session)的消息框架,消息框架允许您将消息临时存储在一个请求中并检索它们以便在后续请求(通常是下一个请求)中显示。 每条消息都标记有确定其优先级的特定级别(例如,infowarningerror)。
message通过中间件和上下文处理器才能实施运行,所以在setting中:

  • INSTALLED_APPS要包含message应用'django.contrib.messages';
  • MIDDLEWARE列表中需要有'django.contrib.sessions.middleware.SessionMiddleware''django.contrib.messages.middleware.MessageMiddleware'
  • TEMPLATES模板的'OPTIONS'配置里要有处理message的上下文处理器'django.contrib.messages.context_processors.messages',否则在HTM模板中找不到message;

消息的存储

Django可以使用不同后台来存储产生的临时消息, 在django.contrib.messages下有3类可以保存消息:

  • class storage.session.SessionStorage:存储请求会话内的所有消息。 因此它需要django.contrib.sessions应用;
  • class storage.cookie.CookieStorage:将消息存储在cookie中(名为messages)会使用hash加密,超过2048字节丢弃旧的消息,能在不同请求中使用消息;
  • class storage.fallback.FallbackStorage:首先使用CookieStorage,若单一的cookie无法满足需求会使用SessionStorage,这也是Django默认的消息存储机制;

这3类都提供了_get_store来读取或保存消息。若要使用其余两种方式进行进行消息存取,在setting中指定存储方式:MESSAGE_STORAGE = 'django.contrib.messages.storage.xxxx'

消息的级别

与Python的logging中的通知级别类似,Django默认的消息可分级为:DEBUG,INFO, SUCCESS,WARNINGERROR;对应的在html模板中消息的标签:debug,info,success,warningerror

添加和显示消息

from django.contrib import messages
messages.add_message(request, messages.INFO, 'Welcome')
messages.error(request, 'Email box full', extra_tags='email')

添加消息,使用add_message(request, level, message, extra_tags='', fail_silently=False)方法,或者使用确定了消息级别的函数来生成消息:message.errorr(),message.info()等。extra_tags可以添加额外的标签,提供给模板进行处理。
在模板中显示消息:

{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
        {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
        {{ message }}
    </li>
    {% endfor %}
</ul>
{% endif %}

在模板中可以使用直接使用messages以及它的属性。
在网页上可以看到生成的消息提示。
在这里插入图片描述

使用Bootstrap样式

  {% if messages %}
    {% for message in messages %}
      {# 原生 Bootstrap #}
      <div class="alert {% if message.tags %} alert-{{ message.tags }}{% endif %}" role="alert">
        {{ message }}
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
    {% endfor %}
  {% endif %}

使用一对<div class="alert alert-<type>" role="alert"></div>标签声名一个消息,消息的长度不限。可以添加一个按钮(dismiss button)来关闭消息提示栏,需要给其添加data-dismiss="alert"属性,显示效果:
在这里插入图片描述
可以使用django-bootstrap 的bootstrap_alert标签代替使用原生的Bootstrap:

  {% if messages %}
    {% for message in messages %}
      {# django-bootstrap #}
      <div>
        {% bootstrap_alert message alert_type=message.tags %}
      </div>
    {% endfor %}
  {% endif %}

自定义消息级别

Bootstrap的alert类型有primarysecondarysuccesswarninginfolightdark共7种,而实质上Django的消息的级别(level)就是个整数,因此可以自定义消息的级别。
django.contrib.messages中的默认设置

DEBUG = 10
INFO = 20
SUCCESS = 25
WARNING = 30
ERROR = 40

DEFAULT_TAGS = {
    DEBUG: 'debug',
    INFO: 'info',
    SUCCESS: 'success',
    WARNING: 'warning',
    ERROR: 'error',
}

DEFAULT_LEVELS = {
    'DEBUG': DEBUG,
    'INFO': INFO,
    'SUCCESS': SUCCESS,
    'WARNING': WARNING,
    'ERROR': ERROR,
}

在setting中自定义消息级别和消息标签:

from django.contrib.messages import constants as message_constants

LIGHT = 2
DARK = 4
PRIMARY = 6
SECONDARY = 8
DANGER = 50

MESSAGE_LEVEL = LIGHT
MESSAGE_TAGS = {
    LIGHT: 'light',
    DARK: 'dark',
    PRIMARY: 'primary',
    SECONDARY: 'secondary',
    DANGER: 'danger'
}

MESSAGE_LEVEL设置了显示消息的最低级别,低于(数值小于)此级别的消息不会显示。
使用自定义消息级别:

from django_ulysses.settings import DANGER, PRIMARY
...
    messages.add_message(request, PRIMARY, 'primary')
    messages.add_message(request, DANGER, 'danger')

显示的效果:
在这里插入图片描述
在注册后,给予一条提示,告知验证邮件已发送:

messages.add_message(request, messages.INFO, '邮件已发送,请查收')
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值