django发送邮件结合itsdangerous+celery+redis
本文讲举一个用户注册发邮件用于激活的例子
1.django默认支持邮件发送
django中django.core.mail模块中有send_mail方法是用来发送邮件使用的,但是不是简单的调用方法就可以发送的,需要借助于第三方的邮件代理服务器,例如126,163等,登上163邮件,在设置里面找到SMTP服务器: smtp.163.com,并生成授权登陆客户端的密码,然后在settings文件中配置
# Email配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 发送邮件的程序 EMAIL_HOST = 'smtp.163.com' # 邮件代理服务器地址 EMAIL_PORT = 25 # 代理服务器端口号 应用层协议 端口号25 固定 EMAIL_HOST_USER = '你的163账号' # 发送人 发送邮件的账号 EMAIL_HOST_PASSWORD = '客户端授权码,不是登陆密码" # 生成的客户端授权密码 开启,163邮箱设置选项中 EMAIL_FROM = '用户激活<你的163账号>' # 显示的发送人信息
2.注册流程和celery异步任务处理流程简述
先来看下用户注册流程,用户提交表单数据,保存数据到数据库,这个时候is_active字典是0,表示邮件没有激活,之后,应该发送邮件,让用户点击激活,但是邮件发送默认是阻塞行为,也就是没有发送成功,程序不会继续往下执行,什么时候推送到代理邮件服务器,什么时候才会有返回值,可能因为网络延迟等问题,让程序停下来,一来用户体验不好,二来这也不是我们想要的,我们想要让发送邮件和下面程序不互相干扰,即我只管发送,成功不成功我不管,继续往下执行,那么我们就需要引入一个异步任务处理的的工具来帮我处理,celery就是一个非常常用和好用,强大,灵活的异步任务处理工具,我们只需要把这个发送邮件的任务交给celery来处理,celery是一个独立的工具,跟Python没有什么关系,跟任何语言都可以结合。
完成异步任务,从角色进行考虑 ,首先是客户端,发布任务,谁提出来的任务谁就是客户端,执行任务的人叫worker。
客户端和celery进行任务传输的中间,celery要求要找一个中间人,你把任务信息交给中间人(broker),celery从中间人这里,取出任务来完成要处理的任务
客户端把任务的名字交给broker,继续执行下面的代码,celery有一个看管的人,去任务队列里面去取任务,celery中也需要任务代码,注意,客户端和celery不一定在一台机器
celery在worker进行设置,默认支持多进程,进程池也可以,协程,gevent,greenletbroker也不是celery实现的,但是官方有推荐,扮演这个角色有哪些,RabbitMQ message queue 消息队列,redis 内存型数据库,存取非常快
客户端发送一个任务,到broker里面,worker去brokder里面去取任务,发送任务的一方并不会关心
什么时候可以完成任务,这是最简单的一个模型
但是有些时候,我现在不需要处理结果但是后面的程序会的到,这个时候我如何在想知道结果的时候还能拿到结果呢 所以现在需要第四方,第四方叫backend
可配置可不配置,backend专门用来存放结果,这个时候需要数据库,对于性能没有要求,celery对backend没有具体推荐,加了backend,celery把处理结果保存到backend里面,如果客户端需要,就去
backend里面去取.
客户端发送任务开启:from tasks import my_task,my_task.delay() 发送任务
worker端开启的方式:celery -A【代表应用app对象】 tasks【任务】 worker --level=info [提示,错误信息以什么样的,级别来显示
celery机制就像生产者消费者模型一样,生产者消费者是一个广义的模型
3.项目代码结构
4.代码,关于itsdangerous生成签名的使用请看上一篇博客
项目下的urls.py
from django.conf.urls import url,include from django.contrib import adminurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^', include("test_app.urls")), ]
应用下的urls.py
from django.conf.urls import url from . import viewsurlpatterns = [url(r'^register$', views.RegisterView.as_view()),url(r'^$', views.IndexView.as_view()),url(r'^active/(?P<token>.+)$', views.ActiveView.as_view()), ]
views.py
from django.shortcuts import render from django.http import HttpResponse from django.views.generic import View from celery_tasks.tasks import send_active_mail from .models import User from django import db from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired from django.conf import settingsclass IndexView(View):def get(self, request):return render(request, "index.html")class RegisterView(View):"""用户注册"""def get(self, request):"""处理get请求, 提供注册页面"""return render(request, "register.html")def post(self, request):"""处理post请求,处理注册数据"""# 获取前端发送的数据/参数name = request.POST.get("name")password = request.POST.get("pass")email = request.POST.get("email")# 使用django的认证系统创建用户try:user = User.objects.create_user(name, email, password)except db.IntegrityError:# 如果抛出此异常,表示用户已经注册return render(request, "register.html", {"errmsg": "用户已注册!"})# 将用户的激活状态设置为假 user.is_active = Falseuser.save()# 生成激活tokentoken = user.generate_active_token()# 使用celery发送邮件 send_active_mail.delay(email, name, token)# 返回给前端结果return render(request, "index.html") # 激活用户 class ActiveView(View):def get(self, request, token):# 根据token 解析,获取用户的id# 创建转换工具(序列化器)s = Serializer(settings.SECRET_KEY, 3600)# 解析try:ret = s.loads(token)except SignatureExpired:# 如果出现异常,表示token过期,返回信息给用户return HttpResponse("激活链接已过期")# 更新用户在数据库中的激活状态user_id = ret.get("confirm")# 查询数据库try:user = User.objects.get(id=user_id)except User.DoesNotExist:# 用户不存在return HttpResponse("用户不存在")user.is_active = True # 更改用户激活状态 user.save()# 返回信息给用户return render(request, "actived.html")
models.py itsdangerous序列化的使用,请看上篇博文
注册数据 保存数据库 生成token 发送邮件
激活:获取token 设置用户的激活状态
把id放入token中 叫签名 因为还可以反推回去
每建立一个django项目,会生成一个很复杂的混淆字符串叫SECRET_KEY
django进行密码加密的时候,除了盐值,通常把这个混淆字符串也加进入了,
盐值+SECRET_KEY+SHA256 才生成一个密码
(,有效期)秒为单位,过期时间
生成token方法和用户对象紧密关联,放入User模型类中
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from django.conf import settings from django.contrib.auth.models import AbstractUser from utils.models import BaseModelclass User(AbstractUser, BaseModel):"""用户"""class Meta:db_table = "users"def generate_active_token(self):"""生成激活令牌"""serializer = Serializer(settings.SECRET_KEY, 3600)token = serializer.dumps({"confirm": self.id}) # 返回bytes类型return token.decode()
celery_tasks下面的tasks.py
注意:
# os.environ["DJANGO_SETTINGS_MODULE"] = "active_mail.settings"
# import django
# django.setup()
celery在启动的时候没有和Django挂钩,虽然这个项目在django目录中的,但是它是用celery启动的,所以他不知道django中的详细配置信息,
因为celery在运行的时候离不开django的环境,所以在celery运行的时候要补充上Django的环境,在celery运行的文件里补充,即任务的文件,
我们希望在启动这个任务的时候把django的所有环境搭建完成,
我们需要给celery运行的机器中设置环境变量,celery寻找信息的时候,按照固定的模式去询问,
os.environ["DJANGO_SETTINGS_MODULE"]="active_mail.settings" 只是在操作系统的环境变量中设置进去了
django,要想运行,需要import django
djagno.setup() 就会去os.environ中找到配置文件,加载所依赖的环境什么是环境变量,操作系统也是一段程序,他运行的时候保存的一些数据就叫环境变量
# coding=utf-8 import osos.environ["DJANGO_SETTINGS_MODULE"] = "active_mail.settings" # import django # django.setup() from django.core.mail import send_mail from celery import Celery from django.conf import settings# 1,创建一个celery对象应用 app = Celery("celery_tasks.tasks", broker="redis://127.0.0.1/6")# 通过使用装饰器,让celery对这个任务进行管理 @app.task def send_active_mail(to_email, user_name, token):subject = "用户激活" # 主题sender = settings.EMAIL_FROM # 发件人receiver = [to_email] # 接收人html_body = '<h1>尊敬的用户 %s, 感谢您注册xx科技!</h1>' \'<br/><p>请点击此链接激活您的帐号<a href="http://127.0.0.1:8000/active/%s">' \'http://127.0.0.1:8000/active/%s<a></p>' % (user_name, token, token)send_mail(subject, "", sender, receiver, html_message=html_body)
utils下面的models.py
from django.db import modelsclass BaseModel(models.Model):"""为模型类补充字段"""create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")class Meta:abstract = True # 说明是抽象模型类
settings.py
""" Django settings for test11 project.Generated by 'django-admin startproject' using Django 1.11.7.For more information on this file, see https://docs.djangoproject.com/en/1.11/topics/settings/For the full list of settings and their values, see https://docs.djangoproject.com/en/1.11/ref/settings/ """import os# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '%m3xt+#4+m1e@#+fmbuk9*38#=s@u0cy0ub&)12x^z*21rhe$t'# SECURITY WARNING: don't run with debug turned on in production! DEBUG = TrueALLOWED_HOSTS = []# Application definition INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','test_app', ]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware', ]ROOT_URLCONF = 'test11.urls'TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, 'templates')],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},}, ]WSGI_APPLICATION = 'test11.wsgi.application'# Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','HOST': '127.0.0.1','PORT': 3306,'USER': 'root','PASSWORD': 'mysql','NAME': 'mail'} }# Password validation # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',}, ]AUTH_USER_MODEL = "test_app.User" # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = 'en-us'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/'STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] # # Email配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 发送邮件的程序 EMAIL_HOST = 'smtp.163.com' # 邮件代理服务器地址 EMAIL_PORT = 25 # 代理服务器端口号 EMAIL_HOST_USER = '你的163邮箱账号' # 发送人 EMAIL_HOST_PASSWORD = '生成的客户端授权码' # 生成的客户端授权密码 EMAIL_FROM = '用户激活<你的163账号>' # 显示的发送人信息
index.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>这是index</h1> </body> </html>
register.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form action="register" method="post">{% csrf_token %}<input type="text" name="name"><input type="password" name="pass"><input type="text" name="email"><input type="submit" value="注册"> </form> </body> </html>
actived.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>已经激活</h1> </body> </html>
5.测试
启动django项目,启动celery,拷贝一份项目代码,在本机其他地方,当然可以是其他机器,cd test11中,使用 celery -A celery_tasks.tasks worker -l info启动
在浏览器中输入http://127.0.0.1:8000/register,填写信息注册,这个时候,mysql数据库中用户表中刚注册的用户is_active字段是0,即False,并且,你注册的邮箱会收到邮件,点击网址,用户激活,再看mysql,发现is_active
变为1,即激活
django发送邮件结合itsdangerous+celery+redis相关推荐
- Django项目实战——8—(判断用户是否登录并返回JSON、Django发送邮件的配置、生成邮箱验证链接、验证邮箱后端逻辑)
1.判断用户是否登录并返回JSON 重要提示: 只有用户登录时才能让其绑定邮箱. 此时前后端交互的数据类型是JSON,所以需要判断用户是否登录并返回JSON给用户. 方案一: 使用Django用户认证 ...
- Django 发送邮件
Django 发送邮件 在settings.py中设置 EMAIL_USE_SSL = True EMAIL_HOST = 'smtp.qq.com' # 如果是 163 改成 smtp.163.c ...
- celery mysql 异步_celery配合rabbitmq任务队列实现任务的异步调度执行[celery redis]
前言: 51cto的文章已经不再补充更新了,另外celery rabbitmq详细的使用方法请到这里浏览. http://xiaorui.cc/2014/11/16/celery-rabbitmq%E ...
- Django使用supervisor管理celery和uwsgi实践记录 uwsgi BACKOFF Exited too quickly (process log may have details)
Django使用supervisor管理celery和uwsgi实践记录 安装下载supervisor不用多说. 直接上配置文件: vir_path标识虚拟环境路径 pro_path标识项目路径 全部 ...
- Celery Redis未授权访问命令执行利用
首发补天社区:https://forum.butian.net/share/224←走过路过帮点一下~ 前言 Celery 是一个简单.灵活且可靠的分布式系统,用于处理大量消息,同时为操作提供维护此类 ...
- Django - 发送邮件
在Python中已经内置了一个smtp邮件发送模块,Django在此基础上进行了简单地封装,让我们在Django环境中可以更方便更灵活的发送邮件. 所有的功能都在django.core.mail中. ...
- Django完成异步工具——celery
情景:用户发起request,并等待response返回.在本些views中,可能需要执行一段耗时的程序,那么用户就会等待很长时间,造成不好的用户体验,比如发送邮件.手机验证码等 使用celery后, ...
- redis将散裂中某个值自增_0基础掌握Django框架(49)Redis
为了更好的学习效果,请搭配视频教程一起学习: Django零基础到项目实战 - 网易云课堂study.163.com redis教程: 概述 redis是一种nosql数据库,他的数据是保存在内存中 ...
- django 1.11.6—— Celery
https://segmentfault.com/a/1190000008022050 1. 环境 python==3.6djang==1.11.6 # 1.8, 1.9, 1.10应该都没问题cel ...
- 手机异步发送短信验证码解决方案-Celery+redis
Celery介绍和使用 一.Celery介绍: 一个简单.灵活且可靠.处理大量消息的分布式系统,可以在一台或者多台机器上运行. Celery是一个功能完备即插即用的任务队列 单个 Celery 进程每 ...
最新文章
- Qt之JSON生成与解析
- 《淘宝网开店 拍摄 修图 设计 装修 实战150招》一一1.17 如何选择合适的拍摄地点...
- 卡通角色表情驱动系列一
- 弹体飞行姿态仿真软件程序代写
- 论文浅尝 | 利用知识图谱嵌入和图卷积网络进行长尾关系抽取
- 智能投影:下一个传统投影的颠覆者
- MORAN文本识别算法开源,刷新多个OCR数据集state-of-the-art
- 十五、static关键字
- appium使用教程python_appium使用教程(一 环境搭建)-------------2.安装部署
- eclipse svn同步代码缓慢、未响应
- 鉴相,鉴频以及环路跟踪算法的理解:
- QCC3040---earbud init module
- android+设置运行内存大小,怎样增大安卓手机的虚拟运行内存RAM ,手机的ram太小....
- 统计|如何理解多元回归下的多重可决系数
- 国内的智能家居品牌有哪些
- docker中容器与容器之间通讯
- 全球首个5G R16 Ready:紫光展锐的新征程
- 国内主要的广告联盟背景
- 数据库中用户登录注册用户信息表怎么设计如何设计
- 基于招聘广告的岗位人才需求分析框架构建与实证研究
热门文章
- 视图状态机制下的IStateManager接口
- 中文分词:正向最大匹配与逆向最大匹配
- sql语法、特殊符号及正则表达式的使用
- mmap函数_Linux中的mmap映射 [二]
- 举例HTML的图像标记,教案html之css滤镜及练习层div块及span标记举例窗口内例题演示功能的实现总结.pdf...
- Matlab中数组的常见用法
- 蓝桥杯2015年第六届C/C++省赛B组第二题-星系炸弹
- PTA 程序设计天梯赛(101~120题)
- 023-数据结构与算法系列
- 【LeetCode 剑指offer刷题】数组题11:228. Summary Ranges