开发模式
普通的开发方式(前后端放在一起写)
前后端分离(Ajax)
后端开发
后端为前端提供URL(API/接口的开发)
注: 返回HttpResponse(render和redirect就用不上)
举例:第一个简单的接口(不依赖任何工具)
反射:
什么是反射:
通过字符串映射object对象的方法或者属性
反射的实际应用场景:
在我们做接口自动化的时候,需要通过不同的请求方式,调用不同的函数
常用四大请求:
get post put delete
rest framework的第一组件
认证:
首先
导入rest_framework.views中的 APIView方法
from rest_framework.views import APIView
我们来看一段伪代码,MyClassView继承了APIView就有了APIView的功能
class APIView(View):
pass
class MyClassView(APIView):
pass
在class类里面的(APIView)在pycharm按住ctrl点击APIView,就会跳到这个方法里面 "例如最后面的大写字母,“DEFAULT_AUTHENTICATION_CLASSES"就是各个组件的配置名”
我们开始进入认证的操作流程:
首先在model中创建两个模型
from django.db import models
# Create your models here.
class UserInfo(models.Model):
"""用户表"""
user_type_choices = (
(1, "普通用户"),
(2, "VIP"),
(3, "SVIP")
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=64)
class UserToken(models.Model):
"""token表"""
user = models.OneToOneField(to="UserInfo")
token = models.CharField(max_length=64)
一个是用户表 一个是token表 用户表中加入了用户类型 token表关联用户表 成一对一的类型 所以一个用户只能有一个token
然后我们开始写view视图 首先我们要创建token生成加密字符
#创建md5函数 继承token模型的user对象
def md5(user):
#导入哈希加密
import hashlib
#导入时间
import time
#变量名ctime 接收现在的时间转为字符str类型
ctime = str(time.time())
#将user对象转为字节并用哈希md5加密 变为16进制字符
m = hashlib.md5(bytes(user, encoding="utf-8"))
#修改并加入时间戳
m.update(bytes(ctime, encoding="utf-8"))
#并返回
return m.hexdigest()
另一种写法
import hashlib
import time
#创建token
def createToken(user):
md5 = hashlib.md5()
#修改账户名字节并加入时间戳 和utf-8(时间戳)
md5.update(bytes(user + str(time.time()), encoding='utf-8'))
#返回摘要,作为十六进制数据字符串值
token = md5.hexdigest()
#返回给token
return token
然后设置登录功能:
#设置登录类
class AuthView(APIView):
# 提交我们用 内置源码的post请求
def post(self,request,*args,**kwargs):
# 设置状态码
ret = {"state_code": 1000, "msg": None}
#判断异常
try:
#原生_request改写request方法 获取前端表单里面的用户名
username = request._request.POST.get("username")
#获取前端表单里面的密码
password = request._request.POST.get("password")
# 用变量名obj 接收数据库里的信息并进行 前端表单与数据库的匹配
obj = models.UserInfo.object.filter(username=username,password=password).first()
#然后进行判断 如果匹配的内容不对
if not obj:
# 就发送状态码 1001
ret["state_code"] = 1001
# 用户名或者 密码错误
ret["msg"] = "用户名或者密码错误"
# 否则为登陆用户创建一个token
token = md5(username)
# 存到数据库 存在就更新,不存在就创建
models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
# 发送状态码
ret["token"] = token
#告知请求成功
ret["msg"] = "请求成功"
#判断异常
except Exception as e:
# 发送状态码
ret["state_code"] = 1002
# 请求异常
ret["msg"] = "请求异常"
#返回JsonResponse
return JsonResponse(ret)
这时我们在 Django项目 我的应用中myapp就是我的应用里面创建 我的工具包 myutils 里面去创建各种组件的方法 这样看着明显而且 省地方! 随后我们创建auth.py来写我们的 认证组件方法:
然后进入 auth.py 导入rest_framework.authentication里面的BaseAuthentication方法,导入应用中的模型类,导入判断不包括在内的方法
from rest_framework.authentication import BaseAuthentication
from myapp import models
from rest_framework import exceptions
#创建一个我的认证类 继承BaseAuthentication方法
class MyAuthtication(BaseAuthentication):
# 设置内置authenticate重写authenticate这个方法
def authenticate(self, request):
# 将用户输入的token用变量接收
token = request._request.GET.get("token")
# 然后在数据库进行匹配
token_obj = models.UserToken.objects.filter(token=token).first()
# 如果认证失败
if not token_obj:
#就返回失败
raise exceptions.AuthenticationFailed("用户认证失败")
# 在 rest framework内部 会将这两个字段赋值给request,以供后续操作使用
#正确就返回用户和token
return (token_obj.user, token)
# 如果pass 就调用原有的方法
class MyAuthtication_None(BaseAuthentication):
def authenticate(self, request):
pass
另一种方法
#创建 我的Authtication
class MyAuthtication(BaseAuthentication):
#使用authenticate方法
def authenticate(self, request):
#获取token
token = request._request.GET.get('token')
#进行判断 如果不是token
if not token:
#返回元组验证失败
raise AuthenticationFailed('未登录')
#否则
else:
#在数据库UserToken表中进行比对 从第一个开始
usertoken = UserToken.objects.filter(token=token).first()
#如果是usertoken
if usertoken:
#就返回用户的token
return (usertoken.user, token)
#否则验证失败
else:
raise AuthenticationFailed('验证失败')
然后我们在settings设置全局配置
在class类里面的(APIView)在pycharm按住ctrl点击APIView,就会跳到这个方法里面 "例如最后面的大写字母,“DEFAULT_AUTHENTICATION_CLASSES"就是各个组件的配置名”
将"DEFAULT_AUTHENTICATION_CLASSES":[设置类的路径]
然后就可以在view视图中加入
authentication_classes = []
如果不在settings配置全局设置 就要单独写这个进行验证 所以 我们要先在view中导入
from myapp.myutils.throttle import MyAuthtication
#将登录类里写入 进行认证
authentication_classes = [MyAuthtication]
就像这样
然后我们试着 在postman 软件中 输入用户名及密码 申请登录尝试我们的代码使用是否正确
输入我们在urls.py中设置的接口 也就是路由
from django.conf.urls import url
from django.contrib import admin
from myapp import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/v1/auth/$', views.AuthView.as_view()),
]
然后输入到postman 中 点击body 中的 X-www-form-urlencoded 输入 用户名 密码进行 登录匹配
登录成功
登录失败
然后我们去看authenticate这个的源码
def authenticate(self, request):
token = request._request.GET.get("token")
首先点击
在class类里面的(APIView)在pycharm按住ctrl点击APIView,然后向下翻找到
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
#进这里 #点击这个
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
找到def dispatch(self, request, *args, **kwargs):中的 self.initial并点击
try:
#进这里 #点击这个
self.initial(request, *args, **kwargs)
然后将会看到:
# 代表 认证组件
self.perform_authentication(request)
# 代表 权限组件
self.check_permissions(request)
# 代表 限流组件
self.check_throttles(request)
然后 点击self.perform_authentication(request)这个方法
在这里它封装了 request.user方法 点击request.user然后选择第三个
在这里封装了self._authenticate() 点击self._authenticate()
------------------------------------------------------------------------------------
rest framework的第二组件
权限:
首先 观察我们之前创建的 用户表 用户分为三种类型 三种类型 就代表在登录 验证 或者订单中来实现 这三种权限显示的内容
我们先在view.py视图中 写好一个订单表 方便在写权限代码时 用权限功能 来使用 订单表 如何用权限显示订单内容
# 创建一个ORDER_DICT字典 输入一些信息
ORDER_DICT = {
1: {
"name": "张三",
"age": 18,
"gender": "男",
"orders": [{
"name": "娃娃",
"price": 1000
}]
},
2: {
"name": "李四",
"age": 20,
"gender": "女",
"orders": [{
"name": "你猜",
"price": 1200
}]
},
}
# 创建OrderView类 继承APIView
class OrderView(APIView):
"""
订单相关业务
"""
# 设置get请求
def get(self, request, *args, **kwargs):
# 状态码
ret = {"state_code": 1000, "msg": None, "data": None}
# 判断异常
try:
# 如果正确 就返回字典的数据
ret['data'] = ORDER_DICT
# 错误就返回状态码
except Exception as e:
pass
return JsonResponse(ret)
然后配置路由urls.py进行配置
from django.conf.urls import url
from django.contrib import admin
from myapp import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 认证接口
url(r'^api/v1/auth/$', views.AuthView.as_view()),
# 订单接口
url(r'^api/v1/order/$', views.OrderView.as_view()),
]
在myapp应用中的工具包myutlis中创建permission.py代表权限组件的配置
# 在rest_framework.permissions 导入BasePermission方法
from rest_framework.permissions import BasePermission
# 创建SVIPPermission类 继承BasePermission方法
class SVIPPermission(BasePermission):
# 状态码
message = "无权访问"
# 重写 has_permission方法
def has_permission(self, request, view):
# 如果 用户 user_type 类型 不等于3
if request.user.user_type != 3:
# 就无法访问
return False
# 否则可以访问
return True
这个 def has_permission(self, request, view):方法从哪来? 我们要看里面的源码:
首先点击 self.dispatch() 找到 self.initial(request,*args,**kwargs)点击 进入
然后点击 代表权限的组件方法
这里会出现 点击这个选择第三个 会出现我们冲的的方法
随后滚动鼠标 会出现这个方法
百度翻译一下
“”"
Allows access only to admin users.
“”"
意思是 仅允许管理员用户访问 所以改写这个方法
随后在views.py里面去配置 订单的权限及认证的组件配置
# 导入的这个负责 认证
# 在 我的应用中从 我的组件包中的 auth 导入MyAuthtication类
from myapp.myutils.auth import MyAuthtication
# 导入的这个负责 权限
# 在 我的应用中从 我的组件包中的 permission 导入SVIPPermission类
from myapp.myutils.permission import SVIPPermission
然后 加入这个 就会加入组件包 来实现 认证 权限两个功能
或者我们在settings.py设置全局组件配置 权限 首先我们要找到源码来写入 这个配置的功能
在class类里面的(APIView)在pycharm按住ctrl点击APIView,然后向下翻找到
DEFAULT_PERMISSION_CLASSES 这个就代表配置 settings.py需要设置的方法 [这里写的是方法类的路径]
然后在view.py订单视图中 写入
class OrderView(APIView):
"""
订单相关业务
"""
authentication_classes = []
permission_classes = []
然后我们用postman进行 实际测试验证
先找到数据库表中 user_type类型为svip的 因为如果是1或者2类型就无法访问
然后进行登录获取token
将获取到的token放在一个文本里 然后在重新打开一个postman的页面 打开配置的路由找到路径 复制这个路径
将这个链接 粘贴到postman 新页面?token=你的文本里面的token
http://127.0.0.1:8000/api/v1/order/?token= 后面放你从文本复制的token
在postman进行验证 输入对的token 就可以获取到表单
输入错误token再次尝试 用户认证失败
在数据库找一个 user_type类型为2 的进行登录 获取token 进行获取订单看看是什么结果
将token输入告诉 无权限访问 那么功能都可以实现
------------------------------------------------------------------------------------
rest framework的第三组件
限流:
限流就是限制访问频率或者次数
首先 在我的应用组件包创建限流 throttle.py文件 在里面设置我们的限流方法
# 从rest_framework.throttling 导入BaseThrottle方法
from rest_framework.throttling import BaseThrottle
# 导入时间
import time
# 设置一个空字典
HISTORY_DICT = {}
# 写MyThrottle类继承 BaseThrottle方法
class MyThrottle(BaseThrottle):
# 初始化self.history
def __init__(self):
self.history = None
# 重写allow_request方法
def allow_request(self, request, view):
# 获取 (META["REMOTE_ADDR"])当前用户的ip
ip_addr = request._request.META["REMOTE_ADDR"]
# 获取当前时间
ctime = time.time()
# 如果 用户当前的ip 不在空字典中
if ip_addr not in HISTORY_DICT:
# 那么就把当前ip赋予一个 现在的时间
HISTORY_DICT[ip_addr] = [ctime, ]
return True
# 如果这条ip在空字典中
history = HISTORY_DICT[ip_addr]
# 初始化的self.history
self.history = history
# 在空字典的ip 找到最后一个ip 是最早的时间那个 小于现在时间的60秒
while history and history[-1] < ctime - 60:
# 就删除最后一个
history.pop()
# 如果ip记录 小于3次
if len(history) < 3:
# 就添加ip 并赋予现在的时间
history.insert(0, ctime)
return True
# 如果访问返回True表示可以继续往下走,False被限制访问
return False
def wait(self):
# 这里是应该返回 剩余的 等待时间
# 要先拿到历史记录
# 当前时间
ctime = time.time()
return 60 - (ctime - self.history[-1])
我们重写了def allow_request(self, request, view):方法 我们可以看看源码是怎样写的
首先点击 self.dispatch() 找到 self.initial(request,*args,**kwargs)点击 进入
然后点击 代表限流的组件方法
在这里会找到封装的allow_request 点击它 选择第三个
这里就会出现allow_request源码的方法 和 wait源码的方法
我们开始在views.py设置 一个类来测试我们书写的代码逻辑是否正确
# 在我的组件包导入 MyThrottle方法
from myapp.myutils.throttle import MyThrottle
class StudentView(APIView):
authentication_classes = [MyAuthtication]
permission_classes = [SVIPPermission]
# 开启这个代表自己设置的限流
throttle_classes = [MyThrottle]
def get(self, request, *args, **kwargs):
# 获取当前的ip
ip = request.META.get('REMOTE_ADDR')
# 并返回当前的ip
return JsonResponse({'ip': ip})
也可以在settings.py全局设置组件
怎么找到它呢 ?
在class类里面的(APIView)在pycharm按住ctrl点击APIView,然后向下翻找到
设置urls.py接口
我们在postman中 先登录 认证 然后获取三次 等待60秒再次获取 是否正常
http://127.0.0.1:8000/api/v1/thor/?token=写入你当前获取的token
一切正常
在工具包的throttle.py里面继续写入
还有一种方法将ip放入缓存 需求是 登录订单一天访问一次 其他登录 一天访问三次
# 从rest_framework.throttling 导入SimpleRateThrottle方法
from rest_framework.throttling import SimpleRateThrottle
# 如果我们要使用 内置的限流类
# SimpleRateThrottle 内置的 写好的 限流组件
class MyThrottle2(SimpleRateThrottle):
scope = "myscope"
# 缓存配置
def get_cache_key(self, request, view):
return self.get_ident(request)
# 订单登录
class UserThrottle(SimpleRateThrottle):
scope = "user_scope"
def get_cache_key(self, request, view):
return request.user.username
settings.py全局设置
这个配置从哪里来? 在这里向上翻
找到这个 这里是相关的一些配置
这个是使用的方法
进入订单登录查看是否 一天只能登录一次
一次 限制
认证登录 每分钟只能访问三次
三次 限制
------------------------------------------------------------------------------------
rest framework的第四组件
版本:
一种是原生的源码
# QueryParameterVersioning点击这个里面会有个这种方法
from rest_framework.versioning import QueryParameterVersioning
点击这个
含义
点击这个选择第三个
复制这个到settings.py进行全局配置
REST_FRAMEWORK = {
"DEFAULT_VERSION": "v1",# 默认版本
"ALLOWED_VERSIONS": ["v1", "v2"],# 被允许访问的版本
}
另一种方法
# 从rest_framework.versioning 导入 URLPathVersioning
from rest_framework.versioning import URLPathVersioning
# 设计一个类
class UsersView(APIView):
versioning_class = URLPathVersioning # 调入版本这个方法
def get(self, request, *args, **kwargs):
# 获取版本
v = request.version
u1 = request.versioning_scheme.reverse(viewname="user",request=request)
print(u1)
return HttpResponse(v)
点击这个URLPathVersioning看源码
from rest_framework.versioning import URLPathVersioning
继承的是这个方法点击
鼠标滚动找到复制这个 放到urls.py路由中
from django.conf.urls import url
from .views import UsersView
# http://127.0.0.1:8000/api/v1/users/ # 推荐
# http://127.0.0.1:8000/api/users/?verison=v1 第一种获取版本的方式 也有公司这种
urlpatterns = [
# url(r'^users/$', UsersView.as_view())
# 并修改成这种形似
url(r'^(?P<version>[v1|v2]+)/users/', UsersView.as_view())
]
反向生成URL
增加一个name参数
url(r'^(?P<version>[v1|v2]+)/users/', views.UsersView.as_view(),name='user')
在postman运行
http://127.0.0.1:8000/api/v1/users/
反向url
------------------------------------------------------------------------------------
rest framework的第五组件
解析器(parser):
序列化:
序列化有两大功能:请求数据的验证,对queryset的序列化
创建URL
url(r'^(?P<version>[v1|v2]+)/roles/$', views.RolesView.as_view()),
编写视图(方法1)
class RolesView(APIView): # 创建类 继承APIView
def get(self,request,*args,**kwargs): # 发起get请求
roles = models.Role.objects.all().values('id','title') # 获取Role模型里面的id和title字段
roles = list(roles) # 将 获取的数据转换成列表
import json # 导入json
ret = json.dumps(roles) # 将queryset数据转换成 json数据
return HttpResponse(ret) # 利用HttpResponse 页面展示出来
DRF序列化:
from rest_framework import serializers
class RolesSerializer(serializers,Serializer):
# 变量名要和数据的字段一致
title = serializers.CharField()
class RolesView(APIView):
def get(self, request, *args, **kwargs):
roles = models.Role.objects.all()
ser = RolesSerializer(instance=roles, many=True) # 调用RolesSerializer类 instance=roles实例获取到的数据 many=True
import json
return HttpResponse(json.dumps(ser.data)) # 数据都在data中
自定义序列化:
创建URL
url(r'^(?P<version>[v1|v2]+)/userinfo/$', views.UserInfoView.as_view())
创建视图
class UserInfoSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
ser = UserInfoSerializer(users, many=True)
import json
return HttpResponse(json.dumps(ser.data))
假设我需要把用户类型也显示出来
class UserInfoSerializer(serializers.Serializer):
user_type = serializers.IntegerField()
显示具体choice中文
class UserInfoSerializer(serializers.Serializer):
xxxx = serializers.IntegerField(source='user_type') # 将user_type字段显示
oooo = serializers.CharField(source='get_user_type_display') # 获取user_type字段的中文
username = serializers.CharField()
password = serializers.CharField()
序列化文章的整个表
from django.db import models
# Create your models here.
class UserGroup(models.Model):
"""用户组模型"""
title = models.CharField(max_length=32, verbose_name="用户组名")
class Role(models.Model):
"""角色模型"""
title = models.CharField(max_length=32, verbose_name="角色名")
class UserInfo(models.Model):
user_type_choices = (
(1, "普通用户"),
(2, "VIP"),
(3, "SVIP")
)
user_type = models.IntegerField(choices=user_type_choices, verbose_name="用户类型")
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=256)
# 外键关系的显示 一对多的外键关系的显示
group = models.ForeignKey(UserGroup, verbose_name="所属用户组")
# 多对多关系的序列化
roles = models.ManyToManyField(Role)
class UserToken(models.Model):
user = models.OneToOneField(UserInfo)
token = models.CharField(max_length=256)
显示指定内容 获取一对多关系
class UserInfoSerializer(serializers.Serializer):
# 获取一对多group表下的id 和 title
gp = serializers.CharField(source="group.id")
gp2 = serializers.CharField(source="group.title")
自定义显示字段

填入数据库
class UserInfoSerializer(serializers.Serializer):
xxxx = serializers.IntegerField(source='user_type')
oooo = serializers.CharField(source='get_user_type_display')
username = serializers.CharField()
password = serializers.CharField()
gp = serializers.CharField(source="group.id")
gp2 = serializers.CharField(source="group.title")
rls = serializers.SerializerMethodField() # 自定义显示
def get_rls(self, row): # row当前行的对象
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({
'id': item.id,
'title': item.title
})
return ret
一般在序列化中我们都继承这个 ModelSerializer 它继承的是Serializer 所以方便使用减少代码
class UserInfoSerializer2(serializers.ModelSerializer):
user_type = serializers.CharField(source='get_user_type_display')
group = serializers.CharField(source="group.title")
rls = serializers.SerializerMethodField() # 自定义显示
def get_rls(self, row): # row当前行的对象
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({
'id': item.id,
'title': item.title
})
return ret
class Meta:
model= models.UserInfo #"username","password"是UserInfo表里的 "user_type",'group','rls' 是上面获取的字段
fields = ("username","password","user_type",'group','rls')
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
# 1、要从数据库里面 取出数据 罗列出所有用户名和密码
users = models.UserInfo.objects.all()
# 2、进行序列化 (序列化的组件)
ser = UserInfoSerializer2(instance=users, many=True)
import json
return HttpResponse(json.dumps(ser.data))
DRF序列化深度控制
# 深度控制 解决了 正常 1对多 多对多
class UserInfoSerializer3(serializers.ModelSerializer):
user_type = serializers.CharField(source='get_user_type_display')
class Meta:
model = models.UserInfo
fields = "__all__"
# 主要这个就是深度控制
depth = 1 # 深度控制,数字越大 取层数越多 但是 效率越低
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
# 1、要从数据库里面 取出数据 罗列出所有用户名和密码
users = models.UserInfo.objects.all()
# 2、进行序列化 (序列化的组件)
ser = UserInfoSerializer3(instance=users, many=True)
import json
return HttpResponse(json.dumps(ser.data))
"""
深度控制 1句话 封装度越高(1句话) 能实现的功能就越固定 灵活性 定制性就差
"""
序列化生成hypermed(给url)
编写URL
url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>\d+)/$', views.GroupfoView.as_view(),name='gp')
查看组的详细
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserGroup
fields = "__all__"
class GroupfoView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.UserGroup.objects.filter(pk=pk).first()
import json
ser = GroupSerializer(instance=obj, many=False)
ret = json.dumps(ser.data)
return HttpResponse(ret)
改进
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
...
ser = UserInfoSerializer(users, many=True,context={'request': request})
...
class UserInfoSerializer(serializers.ModelSerializer):
# 帮我们反向生成URL,相当于我们给了group一个id,它帮我们反向生成一个url
group = serializers.HyperlinkedIdentityField(view_name='gp')
class Meta:
model = models.UserInfo
fields = "__all__"
depth = 0
序列化验证
编写URL
url(r'^(?P<version>[v1|v2]+)/usergroup/$', views.UserGroupView.as_view(), name='ugp'),
编写视图
class UserGroupView(APIView):
def post(self, request, *args, **kwargs):
print(request.data)
return HttpResponse("提交数据")
post提交数据