缓存
在实际项目中,存在大量的数据检索,比如我们刷微博的时候,刚开始加载速度慢一点,然后第一次加载完毕之后,如果你此时的手机没有网络,但是你发现你的微博还是可以照样刷,但是刷到一定的页面就走不动了,那么为什么你在刚开始没有网络的时候还可以接着刷呢?这就是用到了缓存,在你第一次加载的时候,就去数据库中将数据查出来,然后直接缓存到你的移动端,那么我们现在要做的就是将数据从数据库中查出来,然后缓存到内存中,下次来请求的时候,直接去缓存中获取,就不用走数据库了,缓解我的数据库的压力,具体书写如下
views中的书写
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.exceptions import AuthenticationFailed from rest_framework.response import Response from app01 import models from app01 import seria from app01 import auth from django.views.decorators.cache import cache_page import uuid from django.core.cache import cache import time # Create your views here. class Author(APIView): # 只有认证了才可以看 # authentication_classes = [] # permission_classes = [] # throttle_classes = [] # 返回了所有的图书 def get(self, request): author_list = models.User.objects.all() # 进行序列化 user_ser = seria.UserSerializer(instance=author_list, many=True) return Response(user_ser.data) class Login(APIView): authentication_classes = [] permission_classes = [] throttle_classes = [] def post(self, request): back_dic = {'code': 100, 'msg': ''} username = request.data.get('username') password = request.data.get('password') user_type = request.data.get('user_type') try: user_obj = models.User.objects.filter(name=username, pwd=password, user_type=user_type).get() token = uuid.uuid4() # 添加到了数据库 models.Token.objects.update_or_create(user=user_obj, defaults={'token': token}) # 添加到缓存 back_dic['msg'] = '登录成功' back_dic['token'] = token cache.set(token, user_obj.pk, 300) # print(cache.get(token)) except AuthenticationFailed as e: back_dic['code'] = 101 back_dic['msg'] = '用户名或者密码错误' except Exception as e: back_dic['code'] = 102 back_dic['msg'] = '未知错误' return Response(back_dic) # 测试 def test(request): print(1111) return HttpResponse('test ok') @cache_page(5) def index(request): ctime = time.time() author_list = models.User.objects.all() return render(request, 'index.html', locals())
写完之后,我们需要在认证中添加和认证
from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import BasePermission from rest_framework.throttling import SimpleRateThrottle from app01 import models from django.core.cache import cache from rest_framework.exceptions import AuthenticationFailed class Authentications(BaseAuthentication): def authenticate(self, request): # print(request.META) token = request.META.get('HTTP_TOKEN') # 能取到说明是通过了 user_pk = cache.get(token) if user_pk: user_obj = models.User.objects.filter(pk=user_pk).first() # 返回的必须是一个元组 return user_obj, token token_obj = models.Token.objects.filter(token=token).first() # 说明是一个用户 if token_obj: cache.set(token, token_obj.pk, 300) return token_obj.user, token_obj else: raise AuthenticationFailed('您还没有登录呢!') class Permissions(BasePermission): def has_permission(self, request, view): if request.user.user_type == 1: return True else: return False class Throtters(SimpleRateThrottle): scope = 'three' def get_cache_key(self, request, view): return self.get_ident(request)
模板中实现局部缓存的方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> {# 单页面缓存 #} {# 当有局部缓存的时候,需要将原来的装饰器的缓存注释掉 #} <p>{{ ctime }}</p> <ul> {% for author in author_list %} <li>{{ author.name }}</li> {% endfor %} </ul> {# 让他变成局部的缓存 #} {% load cache %} {% cache 1 'test' %} <p>{{ ctime }}</p> {% endcache %} {% cache 2 'aaa' %} <p>{{ ctime }}</p> {% endcache %} <button id="btn">提交</button> </body> <script> $('#btn').click(function () { $.ajax({ url:'http://127.0.0.1:8001/test/', type:'post', data:{'name': 'egon'}, contentType:'application/json', success:function (data) { alert(123); console.log(data) } }) }) </script> </html>
模型表的展示
from django.db import models from django.contrib import auth # Create your models here. class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=64) user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通管理员'), (3, '普通用户')), default=3) class Token(models.Model): user = models.OneToOneField(to='User') token = models.CharField(max_length=64)
在settings中做一些缓存的配置
MIDDLEWARE = [ # 'app01.middlewares.MiddleToken', # 'django.middleware.cache.UpdateCacheMiddleware', # 重写了process_response的方法 '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', # 'django.middleware.cache.FetchFromCacheMiddleware' # 重写了process_request的方法 ] # 全站的只是配置了存的方式,但是没有时间的限制,所以要配置超时时间 如果单页面配置了那就以单页面为准 # CACHE_MIDDLEWARE_SECONDS = 10 # 配置缓存 文件存储的形式 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 指定缓存使用的引擎 'LOCATION': 'D:\mcc\cache', # 指定缓存的路径 'TIMEOUT': 300, # 缓存超时时间(默认为300秒,None表示永不过期) 'OPTIONS': { 'MAX_ENTRIES': 300, # 最大缓存记录的数量(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
然后就可以简单的实现缓存了,但是实际中,我们缓存也是有一个数据库的,我们一般都使用的是redis数据库,这个详细请看上一篇博客,具体项目中运用,我这里使用的是采用短信验证码登录之后将数据放到缓存中
class SMSAPIView(APIView): ''' 1.用户不能连续发多次的短信,得有一个频率的校验-为钱为防爬 ''' throttle_classes = [SMSSimpleRateThrottle] def get(self, request, *args, **kwargs): mobile = request.query_params.get('mobile') # 字段必须的 if not mobile: return Response({ 'status': 2, 'msg': 'mobile参数是必须的' }) # 后台要多mobile数据进行安全校验 if not re.match(r'^1[3-9]\d{9}$', mobile): return Response({ 'status': 2, 'msg': '手机号有误' }) # # 生成随机的验证码 code = '' for i in range(6): code += str(random.randint(0, 9)) # redis 存储 cache.set(mobile, code, constat.SMS_EXPIRE_TIME) # 调用短信第三方发送短信 result = send_sms('176****8046', (code, constat.SMS_EXPIRE_TIME), 1) if not result: return Response({ 'status': 1, 'result': '短信发送失败' }) return Response({ 'status': 0, 'result': '短信发送成功' })
获取的时候可以直接get就好。
跨域问题
什么是跨域? HTTP,域名,端口有一个不同就是跨域,是因为浏览器的同源策略导致这种问题的出现
解决方式一
新建一个django项目,用当前项目的ajax向新项目发送请求
<script> $('#btn').click(function () { $.ajax({ url:'http://127.0.0.1:8001/test/', type:'post', data:{'name': 'egon'}, contentType:'application/json', success:function (data) { alert(123); console.log(data) } }) }) </script>
测试的视图函数
def test(request): # print('1111') # print(request.META.get('HTTP_ORIGIN'), type(request.META.get('HTTP_ORIGIN'))) # Access-Control-Allow-Origin import json # print(111) obj = HttpResponse(json.dumps({'name': 'lqz', 'age': 32})) # * 就是全部都可以匹配 # obj['Access-Control-Allow-Origin'] = '*' # 单个匹配 return obj
然后,再打印的时候发现我们是收到了这个请求,但是发的时候被浏览器阻拦了,那我们就要再新项目中允许别的地址给我发送请求,那么这个方法就是要在回去的时候允许,回去的时候走的方法是process_response,那我们就重写这个方法
from django.utils.deprecation import MiddlewareMixin class Middle(MiddlewareMixin): def process_response(self, request, response): if request.method == 'OPTIONS': # Content-Type response['Access-Control-Allow-Headers'] = 'Content-Type' # obj['Access-Control-Allow-Headers'] = 'Content_Type' http = request.META.get('HTTP_ORIGIN') num = http.split(':')[-1] # print(http) # if num in ['8000', '8001', '8002', '8003', '8004', '8005']: # print(111) # print('222', http) response['Access-Control-Allow-Origin'] = http return response
这样子就做到了跨域问题的解决
解决方式二
调用别人写好的库,然后只需要配置几个参数就好
下载
pip install django-cors-headers
settings中配置
app中注册 INSTALLED_APPS=[ 'corsheaders',] # 允许跨域源,但是由于是所有的都允许,后面什么第三方的网站都可以拿到我的数据 # 所以要改成False CORS_ORIGIN_ALLOW_ALL = False # 设置白名单允许部分跨域 CORS_ORIGIN_WHITELIST = [ # 记住是自己的前端,要携带端口 'http://127.0.0.1:8080', # 一般就是浏览器那里显示什么,这里就是什么 # 'http://localhost:8080' ]
之后我们访问的时候就可以了
短信验证码
我们现在在登录一个网站的时候,大多数人都会采用电话号码登录,然后获得验证码的方式进行使用,我们在项目中也采用了这种方式,然后我使用的是容联云通讯
使用步骤如下
1.直接百度搜索容联云通讯,进行注册
2.注册成功后会给你送免费使用额度
3.查看短信接口
然后我们可以直接去查看短信接口的书写,看完之后如果不懂就看demo,最好的例子
4.下载demo
5.配置修改
将sdk中的两个文件拷贝到我们的项目中,然后修改一些东西,因为这里只支持python2.7,所以要做一些修改
1.导包的地方 from hashlib import md5 import base64 import datetime from urllib import request as urllib2 import json from .xmltojson import xmltojson 2.异常捕获的地方 except Exception as error: if self.Iflog: self.log(url, body, data) return {'172001': '网络错误'} 3.int和str 4.base64 5.端口
具体的我上面没有写完,我只是列出了大概需要改的地方
6.我们使用的时候还要自己新建一个文件,用来重写他的发送短信验证码的方法
from .CCPRestSDK import REST from django.conf import settings # 注:在配置文件dev中完成四个配置信息 # 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID _accountSid = settings.SMS_ACCOUNTSID # 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN _accountToken = settings.SMS_ACCOUNTTOKEN # 说明:请使用管理控制台首页的APPID或自己创建应用的APPID _appId = settings.SMS_APPID # 说明:请求地址,生产环境配置成app.cloopen.com,开发环境配置成sandboxapp.cloopen.com _serverIP = settings.SMS_SERVERIP # 说明:请求端口 ,生产环境为8883 _serverPort = "8883" # 说明:REST API版本号保持不变 _softVersion = '2013-12-26' def send_sms(mobile, code_expire_tuple, temp_id): # 配置 rest = REST(_serverIP, _serverPort, _softVersion) rest.setAccount(_accountSid, _accountToken) rest.setAppId(_appId) # 发送 result = rest.sendTemplateSMS(mobile, code_expire_tuple, temp_id) # 结果:信息成功发生,结果字典result中 statuCode 字段为 "000000" if result.get("statusCode") == "000000": return True # 表示发送短信成功 else: return False # 表示发送失败
7.去settings中配置
# # 短信的配置 荣联云通信配置 SMS_ACCOUNTSID = '你的accountsid' SMS_ACCOUNTTOKEN = '你的token' SMS_APPID = '如果是测试账号,就是未上线,公司的就是上线' SMS_SERVERIP = 'sandboxapp.cloopen.com'
8.测试
import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev') import django django.setup() from luffyapi.libs.yuntongxun import send_sms import random def get_code(): code = "" for i in range(6): code += str(random.randint(0, 9)) return code code = get_code() print(code) result = send_sms('你添加的测试手机号', (code, 5), 1) print(result)
现在就可以正常使用了
短信验证码的频率校验
class SMSAPIView(APIView): ''' 1.用户不能连续发多次的短信 ''' throttle_classes = [SMSSimpleRateThrottle] def get(self, request, *args, **kwargs): mobile = request.query_params.get('mobile') # 字段必须的 if not mobile: return Response({ 'status': 2, 'msg': 'mobile参数是必须的' }) # 后台要多mobile数据进行安全校验 if not re.match(r'^1[3-9]\d{9}$', mobile): return Response({ 'status': 2, 'msg': '手机号有误' }) # # 生成随机的验证码 code = '' for i in range(6): code += str(random.randint(0, 9)) # redis 存储 cache.set(mobile, code, constat.SMS_EXPIRE_TIME) # 调用短信第三方发送短信 result = send_sms('17621948046', (code, constat.SMS_EXPIRE_TIME), 1) if not result: return Response({ 'status': 1, 'result': '短信发送失败' }) return Response({ 'status': 0, 'result': '短信发送成功' })
短信验证码的登录
class LoginMobileAPIView(APIView): def post(self, request, *args, **kwargs): mobile = request.data.get('mobile') code = request.data.get('code') # 从redis中取出我们上面设置的验证码 old_code = cache.get(mobile) if not old_code: return Response({ 'msg': '验证码失效' }) if old_code != code: return Response({ 'msg': '验证码错误' }) # 校验用户的手机号是否合法 try: user = User.objects.get(mobile=mobile) return Response({ 'status': 0, 'msg': '登录成功', 'token': get_jwt_token(user), 'user': UserModelSerializer(user).data }) except: return Response({ 'msg': '手机号还没有注册' })
短信验证码的注册
class RegisterMobileAPIView(APIView): def post(self, request, *args, **kwargs): mobile = request.data.get('mobile') password = request.data.get('password') code = request.data.get('code') old_code = cache.get(mobile) if not old_code: return Response({ 'msg': '验证码失效' }) if old_code != code: return Response({ 'msg': '验证码错误' }) try: User.objects.get(mobile=mobile) return Response({ 'status': 1, 'msg': '用户已存在' }) except: result = User.objects.create_user(mobile=mobile, password=password, username=mobile) if result: return Response({ 'status': 0, 'msg': '注册成功' }) else: return Response({ 'status': 1, 'msg': '注册失败' })