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

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

三、用户系统-注册

只处理后端

from models import UserProfile
import hashlib
# 数据校验 前后端都要做
class UserViews(APIView):

    def post(self,request):
        username = request.data['user']
        nickname = request.data['nick']
        email = request.data['email']
        password_1 = request.data['password_1']
        password_2 = request.data['password_2']
        phone = request.data['phone']

        # 参数基本检查
        if password_1 != password_2:
            return Response("密码不一致")
        # 用户名可不可用
        old_users = UserProfile.objects.filter(username=username)
        if old_users:
            return Response("用户名已被使用")
        # 插入数据(MD5)
        p_m = hashlib.md5()
        p_m.update(password_1.encode())

        UserProfile.objects.create(username=username,nickname=username,password=p_m.hexdigest(),email=email,phone=phone)

        return Response("注册成功")

四、用户系统-登录

views

class LoginViews(APIView):

    def post(self,request):
        username = request.data['user']
        password = request.data['psd']

        user_info = UserProfile.objects.filter(username=username)
        if not user_info:
            return Response("用户不存在")
        
        p_m = hashlib.md5()
        p_m.update(password.encode())
        if p_m.hexdigest() != user_info.first().password:
            return Response("密码不正确")
        return Response("登录成功")

五、用户系统-jwt

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

cookies

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

session

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

2.jwt前传-base64

防君子不防小人

3.jwt前传-HMAC-SH256

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

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

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

import hmac
h = hmac.new(key,str,digestmod='SHA256')
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

import jwt,time
def makeToken(username,expTime=3600*24):
    timeTime = time.time()
    key = '123456'
    payload = {"username":username,"exp":timeTime+expTime}
    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,传递用户的装饰器,写完后只需在函数前加上述@操作

from django.http import JsonResponse
import jwt
from mydemo import settings 
from myapp.models import UserProfile
def logging_check(func):
    def wrap(request,*args,**kwargs):
        # 获取token
        token = request.META.get('HTTP_AUTHOPIZAION')
        if not token:
            return JsonResponse({'code':403,'error':"Please login again"})
        # 校验token
        # 校验jwt
        try:
            res = jwt.decode(token,settings.JWT_TOKEN_KEY)
        except Exception as e:
            return JsonResponse({'code':403,'error':'Please again'})
        
        # 获取登录用户
        username = res['username']
        user = UserProfile.objects.get(username=username)
        request.myuser = user  #  将user传给下面的request

        return func(request,*args,**kwargs)
    return wrap

7.短信接入

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

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

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

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

第三方短信平台

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

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

        案例

xxxx1是1     xxxx2是2   xxxx3是3


import datetime
import hashlib
import base64
import requests         # 发http/https请求
import json

class YunTongXin():

    base_ul = 'https://app.cloopen.com:8883'
    
    def __init__(self,accountSid,accountToken,appId,templateId):
        self.accountSid = accountSid
        self.accountToken = accountToken
        self.appId = appId                  # 应用id  写死
        self.templateId = templateId        # 模板id  写死

    def get_request_url(self,sig):
        self.url = self.base_ul + '/2013-12-26/Accounts/%s/SMS/TemplateSMS?sig=%s'%(self.accountSid,sig)
        return self.url

    def get_timestamp(self):
        # 生成时间戳
        return datetime.datetime.now().strftime('%Y%m%d%H%M%S')

    def get_sig(self,timestamp):
        # 生成业务url中的sig
        s = self.accountSid+self.accountToken+timestamp
        m = hashlib.md5()
        m.update(s.encode())
        return m.hexdigest().upper()
    
    def get_request_header(self,timestamp):
        # 生成请求头
        s = self.accountSid+':'+timestamp
        auth = base64.b64encode(s.encode()).decode()
        return {
            'Accept':'application/json',
            'Content-Type':'application/json;charset=utf-8',
            'Authorization':auth
        }

    def get_request_body(self,phone,code):
        # 生成请求体
        return {
            "to":phone,
            "appId":self.appId,
            "templateId":self.templateId,
            "datas":[code,"3"]
        }

    def get_request_api(self,url,header,body):
        res = requests.post(url,headers=header,data=body)  #res是一个对象
        return res.text             # 响应体的内容

    def run(self,phone,code):
        # 获取时间
        timestamp = self.get_timestamp()
        sig = self.get_sig(timestamp)
        url = self.get_request_url(sig)
        header = self.get_request_header(timestamp)
        # print(url)
        # 生成请求体
        body = self.get_request_body(phone,code)
        # 发请求
        data = self.get_request_api(url,header,json.dumps(body))
        return data

if __name__ == '__main__':

    yun = YunTongXin('xxxx1','xxxx2','xxxx3',1)
    res = yun.run('注册手机',"2341")
    print(res)

验证码功能流程

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

2.后端接到请求后

        1.生成随机验证码

        2.存储验证码->redis

        3.发送验证码

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

生成4位随机数

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

import random
import redis
from tools.sms import YunTongXin
class Sms(APIView):

    def post(self,request):
        phone = request.data['phone']
        random_number = random.randint(0,9999)
        four_digit_number = '{:04d}'.format(random_number)

        conn = redis.Redis(connection_pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000,decode_responses=True,db=15))
        conn.setex(phone,60,four_digit_number)
        yun = YunTongXin('xxxx1','xxxx2','xxxx3',1)
        
        res = yun.run(phone,four_digit_number)
        if res['statusCode'] == '000000':
            return Response('ok')
        else:
            return Response('false')
class SmsCheck(APIView):
    def post(self,request):
        phone = request.data['phone']
        code = request.data['code']

        conn = redis.Redis(connection_pool=redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=1000,decode_responses=True,db=15))
        redis_code = conn.get(phone)
        if not redis_code:
            return Response('code 过期')
        if redis_code != code:
            return Response('验证码错误')
        return Response('ok')

8.celery

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

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

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

能不能celery的标准

1.有没有阻塞

2.实时反馈的不行

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

from celery import Celery
app = Celery('guoxiaonao',
    broker='redis://172.3.3.100:6379/5'
)

# 创建任务函数
@app.task
def task_test():
    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 模型类

from django.db import models
# Create your models here.
class Topic(models.Model):
    title = models.CharField(max_length=50,verbose_name="文章标题")
    category = models.CharField(max_length=20,verbose_name="文章分类")
    limit = models.CharField(max_length=20,verbose_name="文章权限")
    introduce = models.CharField(max_length=90,verbose_name="文章简介")
    content = models.TextField(verbose_name="文章内容")
    created_time = models.DateTimeField(auto_now_add=True)
    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)  去执行视图函数的内容

from django.http import HttpResponse
def cache_set(expire):

    def _cache_set(fun):

        def wrapper(request,*args,**kwargs):
            print(args[0])
            print(args[0].path_info)
            print(args[0].user)
            aa = kwargs['pk']
            if aa == 111:
                # 如果aa == 111   执行视图
                res = fun(request,*args,**kwargs)
                return res
            else:
                return HttpResponse(aa)
        return wrapper

    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 外键

#关联留言和回复
all_messages = Message.objects.filter(topic=author_topic).order_by('-created_time')

msg_list = []
rep_dic = {}
m_count = 0
for msg in all_messages:
    if msg.parent_message:
        #回复
        rep_dic.setdefault(msg.parent_message, [])
        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')})
    else:
        #留言
        m_count += 1
        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':[]})

for m in msg_list:
    if m['id'] in rep_dic:
        m['reply'] = rep_dic[m['id']]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值