• django-项目


    一、RESTful设计风格

    基础概念

    全称:Representational State Transfer

    1.资源

            网络上的一个实体,每个资源都有一个独一无二的URL与之对应;获取资源-直接访问URL即可

    2.表现层

    资源的表现形式    如HTML、xml、JPG、json等

    3.状态转化

    访问一个URL即发生一次客户端和服务端得交互;此次交互将会涉及到数据和状态得变化

    客户端需要通过某些方式接触具体得变化    如GET、POST、PUT、PATCH、DELETE

    设计原则

    1.协议    - http/https

    2.域名

    域名中体现出api字样

    https://api.example.com/v1 或 https://example.org/api/.

    3.版本

    https://api.example.com/v1

     4.路径

    路径中避免使用动词,资源用名词表示

    5.HTTP动词语义

    GET、POST、PUT、PATCH、DELETE

    示例

    6.巧用查询字符串

    7.状态码

    1)用HTTP响应码表达

    2)自定义内部code进行响应

    {’code‘:'00000','msg':'success','data':{}}

    二、用户系统 - ORM

    model

    1. class UserProfile(models.Model):
    2. username=models.BigAutoField(verbose_name="用户名",primary_key=True)
    3. nickname=models.CharField(max_length=20,verbose_name="昵称")
    4. password=models.CharField(max_length=32)
    5. email = models.EmailField()
    6. phone=models.CharField(max_length=11)
    7. avatar=models.ImageField(upload_to='avatar',null=True)
    8. sign=models.CharField(max_length=50,verbose_name="个人签名",default=default_sign)
    9. info=models.CharField(max_length=150,verbose_name="个人简介",default='')
    10. created_time=models.DateTimeField(auto_now_add=True)
    11. updated_time=models.DateTimeField(auto_now=True)
    12. class Meta:
    13. db_table = 'user_user_profile'

    三、用户系统-注册

    只处理后端

    1. from models import UserProfile
    2. import hashlib
    3. # 数据校验 前后端都要做
    4. class UserViews(APIView):
    5. def post(self,request):
    6. username = request.data['user']
    7. nickname = request.data['nick']
    8. email = request.data['email']
    9. password_1 = request.data['password_1']
    10. password_2 = request.data['password_2']
    11. phone = request.data['phone']
    12. # 参数基本检查
    13. if password_1 != password_2:
    14. return Response("密码不一致")
    15. # 用户名可不可用
    16. old_users = UserProfile.objects.filter(username=username)
    17. if old_users:
    18. return Response("用户名已被使用")
    19. # 插入数据(MD5)
    20. p_m = hashlib.md5()
    21. p_m.update(password_1.encode())
    22. UserProfile.objects.create(username=username,nickname=username,password=p_m.hexdigest(),email=email,phone=phone)
    23. return Response("注册成功")

    四、用户系统-登录

    views

    1. class LoginViews(APIView):
    2. def post(self,request):
    3. username = request.data['user']
    4. password = request.data['psd']
    5. user_info = UserProfile.objects.filter(username=username)
    6. if not user_info:
    7. return Response("用户不存在")
    8. p_m = hashlib.md5()
    9. p_m.update(password.encode())
    10. if p_m.hexdigest() != user_info.first().password:
    11. return Response("密码不正确")
    12. return Response("登录成功")

    五、用户系统-jwt

    1.回顾-会话状态的保持

    cookies

            将会话数据存储到浏览器上独立空间,浏览器每次给网站发请求时,如果检测出当前在cookie区域里有站点的数据,就会自动提交到服务器上。

    session

            将会话数据存在服务器上(Django存在数据库中),需要借助cookies,通过sessionId检测

    2.jwt前传-base64

    防君子不防小人

    3.jwt前传-HMAC-SH256

    第一个参数 加密的key,bytes类型

    第二个参数 欲加密的串,bytes类型

    第三个参数 hmac的算法,指定为SHA256

    1. import hmac
    2. h = hmac.new(key,str,digestmod='SHA256')
    3. h.digest()

     4.JWT-json-web-token

    JWT-三大组成

    header

    格式:

    alg代表要使用的算法

    typ表明token的类别 - 必须大写JWT

    {'alg':'HS256','typ':'JWT'}

    payload     分为公有声明和私有声明

    公有声明:提供内置关键字用于描述常见的问题,均可选

    私有声明:可添加自定义的key   如用户名

    signature   根据header中的alg确定具体算法

    5.JWT-校验jwt规则

    1)解析header,确认alg

    2)签名校验-根据传过来的header和payload按alg指明的算法进行签名,将签名结果和传过来的sign进行对比,若对比一致,则校验通过

    3)获取payload自定义内容

    6.Pyjwt

    安装pyjwt(pip3 install)

    encode(payload,key,algorithm)

     payload:这里是字典,需添加公有声明和私有声明

    key:自定义的加密key

    algorithm:需要使用的加密算法

    decode(token,key)

    token:上面生成的token

    key:自定义的加密key

    1. import jwt,time
    2. def makeToken(username,expTime=3600*24):
    3. timeTime = time.time()
    4. key = '123456'
    5. payload = {"username":username,"exp":timeTime+expTime}
    6. return jwt.encode(payload,key,algorithm="HS256")

    5.用户系统-修改个人信息

    6.装饰器校验jwt

            django 提供了一个装饰器  method_decorator,可以将函数的装饰器转换成方法装饰器

    from django.utils.decorators import method_decorator

    @method_decorator(logging_check)

     写一个校验token,传递用户的装饰器,写完后只需在函数前加上述@操作

    1. from django.http import JsonResponse
    2. import jwt
    3. from mydemo import settings
    4. from myapp.models import UserProfile
    5. def logging_check(func):
    6. def wrap(request,*args,**kwargs):
    7. # 获取token
    8. token = request.META.get('HTTP_AUTHOPIZAION')
    9. if not token:
    10. return JsonResponse({'code':403,'error':"Please login again"})
    11. # 校验token
    12. # 校验jwt
    13. try:
    14. res = jwt.decode(token,settings.JWT_TOKEN_KEY)
    15. except Exception as e:
    16. return JsonResponse({'code':403,'error':'Please again'})
    17. # 获取登录用户
    18. username = res['username']
    19. user = UserProfile.objects.get(username=username)
    20. request.myuser = user # 将user传给下面的request
    21. return func(request,*args,**kwargs)
    22. return wrap

    7.短信接入

    1.浏览器点击发送验证码->后端生成随机码并保存在redis(数据在规定时间内有效)

    2.注册创建->后端获取前端输入的验证码

    3.两个短信验证码进行比较

    后端必须知道验证码是多少

    第三方短信平台

            发短信业务-需要接入其他平台【第三方平台】,让短信平台帮助我们发送短信,该服务通过是有偿服务

            容联+云通讯 https://yuntongxun.com

            案例

    xxxx1是1     xxxx2是2   xxxx3是3

    1. import datetime
    2. import hashlib
    3. import base64
    4. import requests # 发http/https请求
    5. import json
    6. class YunTongXin():
    7. base_ul = 'https://app.cloopen.com:8883'
    8. def __init__(self,accountSid,accountToken,appId,templateId):
    9. self.accountSid = accountSid
    10. self.accountToken = accountToken
    11. self.appId = appId # 应用id 写死
    12. self.templateId = templateId # 模板id 写死
    13. def get_request_url(self,sig):
    14. self.url = self.base_ul + '/2013-12-26/Accounts/%s/SMS/TemplateSMS?sig=%s'%(self.accountSid,sig)
    15. return self.url
    16. def get_timestamp(self):
    17. # 生成时间戳
    18. return datetime.datetime.now().strftime('%Y%m%d%H%M%S')
    19. def get_sig(self,timestamp):
    20. # 生成业务url中的sig
    21. s = self.accountSid+self.accountToken+timestamp
    22. m = hashlib.md5()
    23. m.update(s.encode())
    24. return m.hexdigest().upper()
    25. def get_request_header(self,timestamp):
    26. # 生成请求头
    27. s = self.accountSid+':'+timestamp
    28. auth = base64.b64encode(s.encode()).decode()
    29. return {
    30. 'Accept':'application/json',
    31. 'Content-Type':'application/json;charset=utf-8',
    32. 'Authorization':auth
    33. }
    34. def get_request_body(self,phone,code):
    35. # 生成请求体
    36. return {
    37. "to":phone,
    38. "appId":self.appId,
    39. "templateId":self.templateId,
    40. "datas":[code,"3"]
    41. }
    42. def get_request_api(self,url,header,body):
    43. res = requests.post(url,headers=header,data=body) #res是一个对象
    44. return res.text # 响应体的内容
    45. def run(self,phone,code):
    46. # 获取时间
    47. timestamp = self.get_timestamp()
    48. sig = self.get_sig(timestamp)
    49. url = self.get_request_url(sig)
    50. header = self.get_request_header(timestamp)
    51. # print(url)
    52. # 生成请求体
    53. body = self.get_request_body(phone,code)
    54. # 发请求
    55. data = self.get_request_api(url,header,json.dumps(body))
    56. return data
    57. if __name__ == '__main__':
    58. yun = YunTongXin('xxxx1','xxxx2','xxxx3',1)
    59. res = yun.run('注册手机',"2341")
    60. print(res)

    验证码功能流程

    1.前端点击  获取验证码  发送请求到后端

    2.后端接到请求后

            1.生成随机验证码

            2.存储验证码->redis

            3.发送验证码

    3.注册时,需要提交验证码,并在注册逻辑中对比验证码是否正确

    生成4位随机数

    random_number = random.randint(0,9999)
    four_digit_number = '{:04d}'.format(random_number)

    1. import random
    2. import redis
    3. from tools.sms import YunTongXin
    4. class Sms(APIView):
    5. def post(self,request):
    6. phone = request.data['phone']
    7. random_number = random.randint(0,9999)
    8. four_digit_number = '{:04d}'.format(random_number)
    9. conn = redis.Redis(connection_pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000,decode_responses=True,db=15))
    10. conn.setex(phone,60,four_digit_number)
    11. yun = YunTongXin('xxxx1','xxxx2','xxxx3',1)
    12. res = yun.run(phone,four_digit_number)
    13. if res['statusCode'] == '000000':
    14. return Response('ok')
    15. else:
    16. return Response('false')
    1. class SmsCheck(APIView):
    2. def post(self,request):
    3. phone = request.data['phone']
    4. code = request.data['code']
    5. conn = redis.Redis(connection_pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000,decode_responses=True,db=15))
    6. redis_code = conn.get(phone)
    7. if not redis_code:
    8. return Response('code 过期')
    9. if redis_code != code:
    10. return Response('验证码错误')
    11. return Response('ok')

    8.celery

    broker - 消息传输的中间件,生产者一旦有消息发送,将发至broker    broker可以用【RQ,redis】

    blackend - 用于存储消息/任务结果,如果需要跟踪和查询任务状态,则需添加要配置相关

    worker - 工作者 - 消费/执行broker中消息/任务的进程 

    能不能celery的标准

    1.有没有阻塞

    2.实时反馈的不行

    使用 - 创建woker(不使用django)

    1. from celery import Celery
    2. app = Celery('guoxiaonao',
    3. broker='redis://172.3.3.100:6379/5'
    4. )
    5. # 创建任务函数
    6. @app.task
    7. def task_test():
    8. print('task is run ... ')

    使用 - 启动woker

    在tasks.py文件同级目录下执行

    celery -A tasks worker --loglevel=info

    使用 - 创建生产者 - 推送任务

    在tasks.py 文件的同级目录下进入ipython3执行

    from tasks import task_test

    task_test.delay()

    使用 - 存储执行结果 - work

    要想存储执行结果需要添加  backend

    django中使用celery

    1.创建celery配置文件

            项目同名目录下创建celery.py

    2.应用下创建tasks.py集中定义队形worker函数

    3.视图函数充当生产者,推送具体worker函数

    4.项目目录下启动worker

            celery -A 项目同名目录名 worker -l info

    六、文章系统

    模型类

    列表页 - 处理

    方式1:后端给前端,文章全部内容   前端自己截取

    方式2:后端从数据库里获取全部文章内容,截取好后,响应给前端

    方式3:数据库冗余一个字段【简介】,后端只去简介字段内容

    model 模型类

    1. from django.db import models
    2. # Create your models here.
    3. class Topic(models.Model):
    4. title = models.CharField(max_length=50,verbose_name="文章标题")
    5. category = models.CharField(max_length=20,verbose_name="文章分类")
    6. limit = models.CharField(max_length=20,verbose_name="文章权限")
    7. introduce = models.CharField(max_length=90,verbose_name="文章简介")
    8. content = models.TextField(verbose_name="文章内容")
    9. created_time = models.DateTimeField(auto_now_add=True)
    10. updated_time = models.DateTimeField(auto_now=True)

     发表文章

            就是获取前端数据,将数据存入数据库 无技术可言

    列表页

            游客访问只能看到公共的文章,博主自己能访问自己的私有文章

    详情页

            在列表页点击文章标题进入文章详情页面

            获取用户文章的上一篇       

    当前文章id  1

    select * from 表 where id > 1 and people = gxn order by id asc limit 1

            下一篇

    当前文章id  4

    select * from 表 id < 4 and people = gxn order by id desc limit 1

    文章缓存

            典型读多写少

    缓存方案一:cache_page(过期时间s)

    有点:

    缺点:

            1.无法按照具体的方可身份,进行针对性的存储,

            例如:存储的是博主访问自身博客的数据,方可到访是可能会读到 博主删除的缓存

            2.删除缓存成本太高【出现新旧数据不一致】

    缓存方案二:局部 - cache.set/get

    有点:灵活、存储成本最有

    缺点:代码实现成本高

    装饰器缓存 

    判断是否在redis中缓存 如果有直接retrun 没有执行视图然后存储缓存

    res = fun(request,*args,**kwargs)  去执行视图函数的内容

    1. from django.http import HttpResponse
    2. def cache_set(expire):
    3. def _cache_set(fun):
    4. def wrapper(request,*args,**kwargs):
    5. print(args[0])
    6. print(args[0].path_info)
    7. print(args[0].user)
    8. aa = kwargs['pk']
    9. if aa == 111:
    10. # 如果aa == 111 执行视图
    11. res = fun(request,*args,**kwargs)
    12. return res
    13. else:
    14. return HttpResponse(aa)
    15. return wrapper
    16. return _cache_set

    留言功能 

            采用创建父项id的方式实现

    字段名

    类型

    作用

    备注1

    备注2

    id

    int

    主键自增

    content

    varchar(50)

    留言内容

    created_time

    date

    留言创建时间

    parent_message

    int

    该留言的父留言,此ID若存在证明该留言为回复

     int

    publisher_id

    varchar(10)

    留言的发布者

    user_profile外键

    topic_id

    varchar(10)

    文章

    topic 外键

    1. #关联留言和回复
    2. all_messages = Message.objects.filter(topic=author_topic).order_by('-created_time')
    3. msg_list = []
    4. rep_dic = {}
    5. m_count = 0
    6. for msg in all_messages:
    7. if msg.parent_message:
    8. #回复
    9. rep_dic.setdefault(msg.parent_message, [])
    10. rep_dic[msg.parent_message].append({'msg_id':msg.id,'publisher':msg.publisher.nickname, 'publisher_avatr':str(msg.publisher.avatar),'content':msg.content, 'created_time':msg.created_time.strftime('%Y-%m-%d %H:%M:%S')})
    11. else:
    12. #留言
    13. m_count += 1
    14. msg_list.append({'id':msg.id, 'content':msg.content,'publisher':msg.publisher.nickname, 'publisher_avatar':str(msg.publisher.avatar),'created_time':msg.created_time.strftime('%Y-%m-%d %H:%M:%S'), 'reply':[]})
    15. for m in msg_list:
    16. if m['id'] in rep_dic:
    17. m['reply'] = rep_dic[m['id']]

  • 相关阅读:
    Hadoop安装教程
    带你搭一下你的第一个SpringBoot项目
    【数据脱敏方案】不使用 AOP + 注解,使用 SpringBoot+YAML 实现
    刷刷刷——滑动窗口
    中小企业数字化思考:数字化转型应该走自己的路
    bootstarp+springboot基于Java的教学仪器设备商城销售网站_o9b00
    java计算机毕业设计洁能租车源码+mysql数据库+系统+lw文档+部署
    MR案例 - 计算总成绩
    HTML中的DOM事件——鼠标事件、键盘事件、框架对象事件、表单事件
    小谈Springcloud中的几个主流熔断器
  • 原文地址:https://blog.csdn.net/m0_43448734/article/details/132606203