1.1 celery提供异步任务,遇到耗时操作的任务都可以交给celery来完成
1.2 通信过程是,生产者(任务发布者)—>消息队列(broker)(rabbitmq或者是redis)<—消费者(任务执行人), 生产者把任务缓存在消息队列中,消费者从任务队列中把任务取出来执行。
1.3 rabbitmq的安装:https://blog.csdn.net/zhangerfeng333/article/details/106424918
# celery的安装;
pip install -U Celery
# 使用创建主文件main.py
# 1.设置django配置
import os
os.environ["DJANGO_SETTINGS_MODULE"] = '这里是写项目配置位置' # demo.settings.dev
from celery import Celery
# 创建celery 实例对象
celery_app = Celery('demo') # 这里的第一个参数是模块名称,='__main__',第二个参数是broker的url
# 如果想使用Redis作为结果后端,但仍使用RabbitMQ作为消息代理(一种流行的组合):
# app = Celery('tasks', backend='redis://localhost', broker='pyamqp://')
# broker_url= 'amqp://guest:[email protected]:5672'
# 对于大的项目可以使用,单独的配置文件这里的目录是celery_tasks
celery_app.config_from_object('celery_tasks.config')
# 注册任务,自动加载可用的任务
celery_app.autodiscover_tasks(
[
'celery_tasks.任务一',
'celery_tasks.任务二'
]
)
# 使用task装饰器轻松地从任何可调用对象创建任务
@celery_app.task(bind=true,name='send_sms_code',retry_backoff=3)
def send(self,sms_code,**kwargs)
#该bind参数表示该函数将是“绑定方法”,以便您可以访问任务类型实例上的属性和方法。
# bind:保证task对象会作为第一个参数自动传入
# 任务装饰器的bind参数将提供对self(任务类型实例)的访问
# name:异步任务别名
# retry_backoff:异常自动重试的时间间隔 第n次(retry_backoff×2^(n-1))s
# max_retries:异常自动重试次数的上限
# return ****
print(sms_code)
# 启动服务使用
celery -A celery_task.main worker -l info
# -A 指对应的应用程序, 其参数是项目中 Celery实例的位置。
# worker 指这里要启动的worker。
# -l 指日志等级,比如info等级。
# 任务默认是进程执行,如果想要使用高并发,可以使用协程方式执行任务
# 默认是进程池方式,进程数以当前机器的CPU核数为参考,每个CPU开四个进程。
# 如何自己指定进程数:celery worker -A proj --concurrency=4
# 如何改变进程池方式为协程方式:celery worker -A proj --concurrency=1000 -P eventlet -c 1000
pip insatll eventlet
celery -A celery_task.main worker -l info -P eventlet -c 1000
# 调用celery任务使用delay()方法检验参数后,把任务添加到celery
send_sms_code.delay(sms_code,**kwrags)
Celery官方文档
from django.contrib.auth import login,logout
class LoginView(View):
def post(self,request)
user = User.object.create_user(name='',mobile='',password='')
login(request,user)
# login方法实际是django封装了使用session的方法
# 当我们把数据保存到session中他会自动生成一个sessionID
request.session['键'] = '值,把用户的唯一编号:用户id写到session会话中'
#把用户信息保存至 session
#把 session 的 sessionid 存放至 cookie
#把 cookie 放到响应中. 会随着响应返回给前端浏览器
# logout方法实际上是django封装了清除session的方法
logout(request)
# request.session.flush() 清除整条数据
resopnse = http.JsonResponse({
'code':'','errmsg':''})
# 退出登录清除cookie
resopnse.delete_cookie('username')
return response
authenticate
方法需要一个request
参数和一些凭据作为关键字参数。 大多数情况下,代码如下︰
class MyBackend(object):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
当然,它也可以接收token的方式作为参数,在写jwt认证时用得到,例如:
class MyBackend(object):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
1.1 要实现多账号登录需要重写认证后端
# 这里放的是用户的认证后端
from django.contrib.auth.backends import ModelBackend
import re
from .models import User
# 验证手机号或者密码的方法
def get_username_mobile(account):
try:
# 如果和手机号匹配,则使用手机号作为验证方法
if re.match(r''^1[3-9]\d{
9}$',account):
user = User.object.get(mobile=account)
else:
user = User.object.get(username=account)
# get方法查询不到是会抛出User.DoesNotExist 异常
except User.DoesNotExist:
return None
else:
return user
# 继承ModelBackends,重写authenticate方法
class UsernameMobileModelBackends(ModelBackends):
# 重写authenticate方法
def authenticate(self,request,username=None,password=None,**kwargs):
user = get_username_mobile(username)
# 校验 user 是否存在并校验密码是否正确
if user and user.check_password(password):
# 如果user存在, 密码正确, 则返回 user
return user
1.2 用户登录判断request.user.is_authenticated可以判断用户是否登录,True登录,False未登录
# 利用扩展类来判断用户是否登录
def decorator(func):
def inner(request,*args,**kwargs):
if request.user.is_authenticated:
return func(request,**args,**kwargs)
else:
# 如果用户未登录,则进入这里,返回400的状态码
return http.JsonResponse({
'code':400,
'errmsg':'请登录后重试'})
return inner
# 重写as_view方法,给as_view加上装饰器。
class LoginRequired(object):
@classmethod
def as_view(cls,*args,**kwargs):
view = super().as_view(*args,**kwargs)
view = decorator(view)
return view
# 异步发送邮件的方法
"""
1.利用celery异步发送邮件,
2.定义的任务中使用django自带的send_mail方法
3.生成邮箱验证链接,也就是绑定token(注意token值要和用户的id和邮箱进行绑定,一个用户一个验证)
4.用户携带token访问邮箱激活地址,对token进行解密,根据用户id和email查询用户信息,返回用户信息完成验证
5.验证完毕修改字段active_email=True
"""
pip install -U Celery
import os
# 设置django配置
os.environ['DJANGO_SETTINGS_MODEL'] = 'demo.settings.dev'
from celery import Celery
celery_app = Celery('demo')
# broker 是中间人,消息队列url,可以是rabbitmq和redis
# broker_url= 'amqp://guest:[email protected]:5672'
celery_app.config_from_object('celery_tasks.config')
celery_app.autodiscover_tasks([
'celery_tasks.email',
])
from django.core.mail import send_mail
from django.conf import settings
from celery.main import celery_app
# 这个配置在email的文件中
@celery_app.task(bind=True,name='send_email_code',retry_backoff=3)
def send_verify_email(self,to_email,verify_url):
"""
verify_url 是用户的邮箱激活链接
"""
html_message = '尊敬的用户您好!
' \
'感谢您使用美多商城。
' \
'您的邮箱为:%s 。请点击此链接激活您的邮箱:
' \
'' % (to_email, verify_url, verify_url)
result = send_mail(subject,'',settings.EMIL_FROM,[to_email],html_message=html_message)
# 调用方法
send_sms_code.delay(mobile,sms_code,**kwargs)
# 进程启动celery
celery -A celery_task.main worker -l info
# 协程启动celery,指明协程池数据
celery -A celery_task.main worker -l info -P eventlet -c 1000
# 生成验证邮箱
# 在 users.models.py 文件中, 添加如下代码:
# 导入:
from itsdangerous import TimedJSONWebSignatureSerializer
from django.conf import settings
def generate_verify_email_url(user):
"""
生成邮箱验证链接
:param user: 当前登录用户
:return: verify_url
"""
# 调用 itsdangerous 中的类,生成对象
# 有效期: 1天
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY,
expires_in= 60 * 60 * 24)
# 拼接参数
data = {
'user_id': user.id, 'email': user.email}
# 加密生成 token 值, 这个值是 bytes 类型, 所以解码为 str:
token = serializer.dumps(data).decode()
# 拼接 url
verify_url = settings.EMAIL_VERIFY_URL + token
# 返回
return verify_url
# 邮箱验证逻辑
verify_url = generate_verify_email_url(request.user)
send_verify_email.delay(email, verify_url)
# 对token进行解码获取当前用户。
def check_verify_email_token(token):
"""
验证token并提取user
:param token: 用户信息签名后的结果
:return: user, None
"""
# 调用 itsdangerous 类,生成对象
# 邮件验证链接有效期:一天
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY,
expires_in=60 * 60 * 24)
try:
# 解析传入的 token 值, 获取数据 data
data = serializer.loads(token)
except BadData:
# 如果传入的 token 中没有值, 则报错
return None
else:
# 如果有值, 则获取
user_id = data.get('user_id')
email = data.get('email')
# 获取到值之后, 尝试从 User 表中获取对应的用户
try:
user = User.objects.get(id=user_id,
email=email)
except User.DoesNotExist:
# 如果用户不存在, 则返回 None
return None
else:
# 如果存在则直接返回
return user
Django.core.mail 模块给我们提供了 send_mail
函数来发送邮件:
send_mail
(subject, message, from_email, recipient_list,html_message=None)
subject 邮件标题
message 普通邮件正文, 普通字符串
from_email 发件人
recipient_list 收件人列表
html_message 多媒体邮件正文,可以是html字符串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q8x1b4Gu-1593442358248)(QQ登录时序图.png)]
# 导入:
from django.db import models
from meiduo_mall.utils.BaseModel import BaseModel
# 定义QQ登录的模型类:
class OAuthQQUser(BaseModel):
"""QQ登录用户数据"""
# user 是个外键, 关联对应的用户
user = models.ForeignKey('users.User',
on_delete=models.CASCADE,
verbose_name='用户')
# qq 发布的用户身份id
openid = models.CharField(max_length=64,
verbose_name='openid',
db_index=True)
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登录用户数据'
verbose_name_plural = verbose_name
# 安装 QQLoginTool 工具类:
pip install QQLoginTool
# 导入:
from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django import http
from django.views import View
class QQURLView(View):
"""提供QQ登录页面网址"""
def get(self, request):
# next 表示从哪个页面进入到的登录页面
# 将来登录成功后,就自动回到那个页面
next = request.GET.get('next')
# 获取 QQ 登录页面网址
# 创建 OAuthQQ 类的对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI,
state=next)
# 调用对象的获取 qq 地址方法
login_url = oauth.get_qq_url()
# 返回登录地址
return http.JsonResponse({
'code': 0,
'errmsg': 'OK',
'login_url':login_url})
这里是 QQ 登录的第二次请求接口:
提示:
用户在 QQ 登录成功后,QQ 会将用户重定向到我们配置的回调网址.
在 QQ 重定向到回调网址时,会传给我们一个 Authorization Code 的参数.
我们需要拿到 Authorization Code 并完成 OAuth2.0 认证获取 openid
在本项目中,我们申请 QQ 登录开发资质时配置的回调网址为:
http://www.meiduo.site:8000/oauth_callback
QQ 互联重定向的完整网址为:
http://www.meiduo.site:8000/oauth_callback/?code=AE263F12675FA79185B54870D79730A7&state=%2F
# qq操作相关步骤如下
"""
1.点击qq图标后发送ajax请求给django服务器,服务器返回qq授权地址页面,
2. 打开地址访问qq授权页面,验证登录是否正确,验证正确返回code给django服务器,
2.2.django服务器,携带code向qq服务器获取atoken
2.3.django服务器,携带atoken向qq服务器获取openid
2.4.判断用户是不是第一次登录,如果不是直接登录成功,返回jsw token给前端
'''这里对出于安全考虑对openid进行加密处理,防止泄露,可以使用django自带的(itsdangerous
使用 TimedJSONWebSignatureSerializer 可以生成带有有效期的 token)(也可以使用JWT进行加密处理)
2.5是第一次登录直接对openid进行加密生成access token并返回 ,跳转到state页面,进行注册逻辑
3.2 注册后,注册信息和access token一起携带过来,对access token进行解密处理得到openid,数据保存到
用户表和qq表中。完成qq第三方登录逻辑。
"""
from QQLoginTool.QQtool import OAuthQQ
from django.contrib.auth import login
from django.shortcuts import render
from django.views import View
from django.conf import settings
from django.http import JsonResponse
import logging
import json, re
from carts.utils import merge_cookie_to_redis
from oauth.models import OAuthQQUser
from oauth.utils import generate_access_token_by_openid, check_access_token
from django_redis import get_redis_connection
from users.models import User
logger = logging.getLogger('django')
class QQURLView(View):
def get(self, request):
'''QQ登录的第一个接口, 返回qq的网址'''
# 1.接收查询字符串参数
next = request.GET.get('next')
# 2.用QQLoginTool工具类创建对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI,
state=next)
# 3.利用对象, 调用函数, 获取qq登录的地址
url = oauth.get_qq_url()
# 4.返回json(code, errmsg, login_url)
return JsonResponse({
'code':0,
'errmsg':'ok',
'login_url':url})
class QQURLSecondView(View):
def get(self, request):
'''qq登录的第二个接口'''
# 1.接收参数(查询字符串类型)
code = request.GET.get('code')
# 2.判断参数是否存在
if not code:
return JsonResponse({
'code':400,
'errmsg':"code不存在"})
# 3.获取QQLoginTool工具类的对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 4.调用对象的方法, 获取access_token
access_token = oauth.get_access_token(code)
# 5.调用对象的方法, 根据access_token获取openid
openid = oauth.get_open_id(access_token)
except Exception as e:
logger.error(e)
return JsonResponse({
'code':400,
'errmsg':"获取openid出错"})
try:
# 6.根据openid这个条件, 去到QQ表中获取对应的对象
oauth_qq = OAuthQQUser.objects.get(openid=openid)
except Exception as e:
# 11.如果该对象获取不到, 说明qq表中不存在该记录
# 12.封装一个函数: 把openid加密为access_token
access_token = generate_access_token_by_openid(openid)
# 13.把access_token返回给前端
# 返回的code不要为:0,因为前端设置了: 返回0进入首页.
return JsonResponse({
'code':300,
'errmsg':'ok',
'access_token':access_token})
else:
# 7.如果该对象获取到了. 算是登录成功
user = oauth_qq.user
# 8.实现状态保持
login(request, user)
response = JsonResponse({
'code':0,
'errmsg':'ok'})
# 9.设置cookie(username)
response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
# 10.返回响应
# 增加合并购物车功能:
response = merge_cookie_to_redis(request, response)
# 12.返回结果(json)
return response
def post(self, request):
'''qq登录的第三个接口'''
# 1.接收参数(json)
dict = json.loads(request.body.decode())
mobile = dict.get('mobile')
password = dict.get('password')
sms_code_client = dict.get('sms_code')
access_token = dict.get('access_token')
# 2.总体检验,查看是否为空
if not all([mobile, password, sms_code_client, access_token]):
return JsonResponse({
'code':400,
'errmsg':'缺少必传参数'})
# 3.mobile单个检验
if not re.match(r'^1[3-9]\d{9}$', mobile):
return JsonResponse({
'code': 400,
'errmsg': 'mobile格式有误'})
# 4.password单个检验
if not re.match(r'^[a-zA-Z0-9]{8,20}$', password):
return JsonResponse({
'code': 400,
'errmsg': 'password格式有误'})
# 5.链接redis, 获取redis的链接对象
redis_conn = get_redis_connection('verify_code')
# 6.从redis中获取服务端的短信验证码
sms_code_server = redis_conn.get('sms_%s' % mobile)
# 7.判断服务端的短信验证码是否过期
if not sms_code_server:
return JsonResponse({
'code': 400,
'errmsg': '短信验证码过期'})
# 8.对比前后端的短信验证码
if sms_code_client != sms_code_server.decode():
return JsonResponse({
'code': 400,
'errmsg': '输入的短信验证码有误'})
# 9.自定义一个函数,把access_token解密:openid
openid = check_access_token(access_token)
# 10.判断openid是否存在,如果存在没问题
if openid is None:
return JsonResponse({
'code': 400,
'errmsg': 'openid为空'})
try:
# 11.从User表中获取一个该手机号对应的用户
user = User.objects.get(mobile=mobile)
except Exception as e:
# 12.如果该用户不存在, 给User增加一个新的记录
user = User.objects.create_user(username=mobile,
password=password,
mobile=mobile)
else:
# 13.如果该用户存在, 比较密码是否一致
if not user.check_password(password):
return JsonResponse({
'code': 400,
'errmsg': '密码输入的不对'})
# 14.把openid和user保存到QQ表中
try:
OAuthQQUser.objects.create(openid=openid,
user=user)
except Exception as e:
return JsonResponse({
'code': 400,
'errmsg': '保存到qq表中出错'})
# 15.状态保持
login(request, user)
response = JsonResponse({
'code':0,
'errmsg':'ok'})
# 16.设置cookie:username
response.set_cookie('username', user.username, max_age=3600 * 24 * 14)
# 17.返回json
# 增加合并购物车功能:
response = merge_cookie_to_redis(request, response)
# 12.返回结果(json)
return response
from itsdangerous import TimedJSONWebSignatureSerializer
from django.conf import settings
def generate_access_token(openid):
"""对传入的 openid 进行加密处理, 返回 token"""
# QQ 登录保存用户数据的 token 有效期
# settings.SECRET_KEY: 加密使用的秘钥
# 过期时间: 600s = 10min
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY,
expires_in=600)
data = {
'openid': openid}
# 对 dict 进行加密
token = serializer.dumps(data)
# 加密完之后, 解码返回.
return token.decode()
# 解密方式
from itsdangerous import BadData
# 定义函数, 检验传入的 access_token 里面是否包含有 openid
def check_access_token(access_token):
"""
检验用户传入的 token
:param token: token
:return: openid or None
"""
# 调用 itsdangerous 中的类, 生成对象
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY,
expires_in=600)
try:
# 尝试使用对象的 loads 函数
# 对 access_token 进行反序列化( 类似于解密 )
# 查看是否能够获取到数据:
data = serializer.loads(access_token)
except BadData:
# 如果出错, 则说明 access_token 里面不是我们认可的.
# 返回 None
return None
else:
# 如果能够从中获取 data, 则把 data 中的 openid 返回
return data.get('openid')
我们以 Docker 官方加速器https://registry.docker-cn.com
为例进行介绍。
请在/etc/docker/daemon.json
中写入如下内容(如果文件不存在请新建该文件)
{
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}
注意,一定要保证该文件符合 json 规范,否则 Docker 将不能启动。
之后重新启动服务。
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
命令行执行docker info
,如果从结果中看到了如下内容,说明配置成功。
Registry Mirrors:
https://registry.docker-cn.com/
# 注意切换到root用户可以不用sudo
# docker命令
sudo service docker start --启动docker
sudo service docker stop --停止docker
sudo service docker restart --重启docker
# 或者
sudo systemctl start | restart| stop docker --也是docker的相关命令
# docker镜像
sudo docker images -a --查看所有镜像
sudo docker images ubuntu --查看ubuntu镜像
sudo docker search 【镜像名称】 --查找镜像
sudo docker pull 镜像 -- 下拉镜像
docker tag [old_image]:[old_version] [new_image]:[new_version] -- 镜像重命名
docker rmi [image_id / image_name:image_version] -- 删除镜像
# 将已经下载好的镜像,导出到本地,以备后用。
命令格式:
docker save -o [包文件] [镜像]
docker save [镜像1] ... [镜像n] > [包文件]
注意:
docker save 会保存镜像的所有历史记录和元数据信息
# 导入镜像命令格式:
docker load < [image.tar_name]
docker load -i [image.tar_name]
注意:
docker load 不能指定镜像的名称
# 查看镜像历史命令格式:
docker history [image_name]
# docker容器
sudo docker ps -a --查看所有(包括已经停止的容器,不加-a则不包含停止的容器)容器
sudo docker container ls --查看容器
sudo docker inspect [容器id/容器名] -- 查看容器详细信息
sudo docker stop| restart | start 容器名/容器ID --停止|重启|开启容器
sudo docker logs [容器id/容器名] -- 查看容器日志信息
sudo docker rm 容器名/容器ID --正常删除容器
sudo docker rm -f --强行删除正在运行的容器
# 创建并启动一个容器
sudo docker run -itd --name=容器名 --net=host -v[宿主机目录]:[容器目录] 镜像名/bin/bash
-t 启动一个为终端
-i 交互式启动
-d 守护进程方式启动
--name 给容器起名字
--net=host 使用宿主机的ip
# 进入正在运行的容器
sudo docker exec -it 容器名
# 基于容器创建镜像
sudo docker commit -m '改动信息' -a '作者姓名' 【容器名】 【镜像名:版本号】
、
# dockerfile
1.创建 touch Dockerfile 文件
2.写入Dockerfile指令
FROM 镜像名 python:3.6
WORKDIR 指定当前工作目录 /django
RUN 执行命令行的命令 pip install requeirment.text
ENV 设置环境变量
ADD 高级复制
COPY 复制 ./demo ./
EXPOSE 指明端口号 8000
CMD 执行的命令 例如['python','manage.py','runserver','0.0.0.0:8000']
3.docker build -t 基础镜像名
# docker进行项目部署
FROM python:3.5
LABEL maintainer="[email protected]"
RUN pip install django==1.11.11
WORKDIR /code
COPY ./bookmanager ./
EXPOSE 8000
CMD ["python3","manage.py","runserver","0.0.0.0:8000"]
linux自带的crontab模块,执行定时任务
在flask中我们使用APSheduler
haystack自动实现了elasticseach 把mysql数据库的数据存在里面,在配置中指定elasticsearch的数据更新时间
倒排索引,inidces 索引,mapping,映射,id 具体数据,文档。。。
索引文档不可以修改,一旦修改就要从新建立,还有主分片创建后也不可以更改,但是可以更改副分片
乐观锁并不是真实存在的锁,而是在更新的时候判断此时的库存是否是之前查询出的库存,如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不再执行库存更新。类似如下操作
update tb_sku set stock=2 where id=1 and stock=7;
SKU.objects.filter(id=1, stock=7).update(stock=2)
# 在执行sql语句之前进行一次查询,把查询结果进行记录下来,比如a=15
# 死循环,执行sql语句时在把上一次的查询的记录a=15当做条件进行查询,如果不满足就返回不存在,满足返回结果
# 缺点是牺牲性能
悲观锁,容易死锁
消息队列(例如celery),一个一个执行,串行化处理,不并行,一个任务执行
事务的一致性,在同时保存两张垂直分表时使用的方法。 在django中的itdangerous 她是危险的 atmic
事务保存
事务回滚
事务提交
mysql_master
mysql_slave
1.创建数据库读写路由
- 在
meiduo_mall.utils.db_router.py
中实现读写路由
class MasterSlaveDBRouter(object):
"""数据库读写路由"""
def db_for_read(self, model, **hints):
"""读所使用的服务器:"""
return "slave"
def db_for_write(self, model, **hints):
"""写所使用的服务器:"""
return "default"
def allow_relation(self, obj1, obj2, **hints):
"""是否运行关联操作"""
return True
2.配置数据库读写路由
在 dev.py 文件中配置如下参数
DATABASE_ROUTERS = ['meiduo_mall.utils.db_router.MasterSlaveDBRouter']
在django 中的 restframework-jwt模块
# 登录时自动签发token
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^authorizations/$', obtain_jwt_token),
]
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'id': user.id,
'username': user.username
}
# 配置条件如下
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
# JWT配置
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response.jwt_response_payload_handler',
}
# 方法二,手动签发token
from rest_framework_jwt.setting.api_settings
class CreateUserSerializer(serializers.ModelSerializer):
"""创建用户的序列化器"""
sms_code = serializers.CharField(label='短信验证码', write_only=True)
token = serializers.CharField(label='JWT token', read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'password', 'sms_code', 'mobile', 'token','avatar')
extra_kwargs = {
'username': {
'min_length': 5,
'max_length': 20,
'error_messages': {
'min_length': '仅允许5-20个字符的用户名',
'max_length': '仅允许5-20个字符的用户名',
}
},
'password': {
'write_only': True,
'min_length': 8,
'max_length': 20,
'error_messages': {
'min_length': '仅允许8-20个字符的密码',
'max_length': '仅允许8-20个字符的密码',
}
}
}
# 单个字段验证
def validate_mobile(self, value):
"""验证手机号"""
if not re.match(r'^1[3-9]\d{9}$', value):
raise serializers.ValidationError('手机号格式错误')
return value
# 比较验证
def validate(self, data):
# 判断短信验证码
redis_conn = get_redis_connection('verify_codes')
mobile = data['mobile']
real_sms_code = redis_conn.get('sms_%s' % mobile)
if real_sms_code is None:
raise serializers.ValidationError('无效的短信验证码')
if data['sms_code'] != real_sms_code.decode():
raise serializers.ValidationError('短信验证码错误')
return data
def create(self, validated_data):
"""重写保存方法,增加密码加密"""
# 移除数据库模型类中不存在的属性
del validated_data['sms_code']
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
# 手动签发jwt token
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token
return user
头部声明的加密方法HMACSHA256(base64(header) + .+base64(payload) ,secret)
import jwt
from flask import current_app
def generate_jwt(payload, expiry, secret=None):
"""
生成jwt
:param payload: dict 载荷
:param expiry: datetime 有效期
:param secret: 密钥
:return: jwt
"""
# 设置过期时间
_payload = {
'exp': expiry}
# 把载荷部分追加到这个字典里面
_payload.update(payload)
if not secret:
secret = current_app.config['JWT_SECRET']
# 加密生成token
token = jwt.encode(_payload, secret, algorithm='HS256')
return token.decode()
# 验证token
def verify_jwt(token, secret=None):
"""
检验jwt
:param token: jwt
:param secret: 密钥
:return: dict: payload
"""
if not secret:
secret = current_app.config['JWT_SECRET']
try:
# token解码得到
payload = jwt.decode(token, secret, algorithm=['HS256'])
except jwt.PyJWTError:
payload = None
return payload
# 配置DRF相关
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 分页处理类
'PAGE_SIZE': 5, # 每页数目
# 'EXCEPTION_HANDLER': 'tensquare.exceptions.custom_exception_handler', # 整体的异常处理类
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 24 * 60 * 60,# 缓存时间
'DEFAULT_USE_CACHE': 'default', # 缓存存储
'DEFAULT_CACHE_ERRORS': False,
# 认证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
在Django REST framework中使用缓存,可以通过drf-extensions扩展来实现。
drf-extensions组件内部提供了 DRF 的本地内存方式的缓存方式,本地内存方式缓存在项目重启后则会消失.
pip install drf-extensions
redis集群搭建 和redis主从同步实现缓存
缓存的本质是提高效率,减少数据库查询压力
缓存原理
缓存方式
缓存穿透
缓存雪崩
sqlachemy框架实现关系型映射
操作语句
数据库表设计
sql语句
事务,主从同步,读写分离,分布式,垂直分表,水平分表,数据库优化,索引
redis 缓存用集群,持久性储存用主从 pipeline 支持一定的事务
JWT的构建,
定时任务,APsheduler
rpc 远程调用方法,推荐系统智能推荐系统
即时通讯,websocket socket.IO 聊天机器人,即时消息
elasticsearch logstash导入数据
单元测试
部署相关
ination.PageNumberPagination’, # 分页处理类
‘PAGE_SIZE’: 5, # 每页数目
# ‘EXCEPTION_HANDLER’: ‘tensquare.exceptions.custom_exception_handler’, # 整体的异常处理类
‘DEFAULT_CACHE_RESPONSE_TIMEOUT’: 24 * 60 * 60,# 缓存时间
‘DEFAULT_USE_CACHE’: ‘default’, # 缓存存储
‘DEFAULT_CACHE_ERRORS’: False,
# 认证
‘DEFAULT_AUTHENTICATION_CLASSES’: (
‘rest_framework_jwt.authentication.JSONWebTokenAuthentication’,
‘rest_framework.authentication.SessionAuthentication’,
‘rest_framework.authentication.BasicAuthentication’,
),
}
#### 缓存的实现方式
```python
在Django REST framework中使用缓存,可以通过drf-extensions扩展来实现。
drf-extensions组件内部提供了 DRF 的本地内存方式的缓存方式,本地内存方式缓存在项目重启后则会消失.
pip install drf-extensions
redis集群搭建 和redis主从同步实现缓存
缓存的本质是提高效率,减少数据库查询压力
缓存原理
缓存方式
缓存穿透
缓存雪崩
sqlachemy框架实现关系型映射
操作语句
数据库表设计
sql语句
事务,主从同步,读写分离,分布式,垂直分表,水平分表,数据库优化,索引
redis 缓存用集群,持久性储存用主从 pipeline 支持一定的事务
JWT的构建,
定时任务,APsheduler
rpc 远程调用方法,推荐系统智能推荐系统
即时通讯,websocket socket.IO 聊天机器人,即时消息
elasticsearch logstash导入数据
单元测试
部署相关