Django rest framework
开发模式
- 普通开发模式(前后端不分离,前后端一起写)
- 前后端分离
后端开发
- 为前端提供URL(API/接口开发)
- 永远返回HttpResponse
Django视图
django中的视图分为两类
- CBV :class base view (基于类的视图)
- FBV:function base view (基于函数的视图)
- url 可以直接根据视图函数FBV 进入 找到对应的方法 GET、POST等 但是CBV是如何实现的呢?
- 答案是反射
# FBV, function base view 基于函数的视图
def user(request):
if request.method == 'GET'
user_list = ['ales', '123']
return HttpResponse(json.dumps(user_list))
# CBV, class base view 基于类的视图
from django.views import View
class StudentsView(View):
def get(self, request):
return HttpResponse('GET')
def post(self, request):
return HttpResponse('post')
def put(self, request):
return HttpResponse('put')
def delete(self, request):
return HttpResponse('delete')
Django视图之CBV源码剖析
CBV:基于反射实现根据请求方式不同,执行不同的方法
CBV实现基本流程图(反射)
# 原理 : url -> view方法 -> dispatch(反射执行其他:GET/POST/PUT/DELETE)方法
# CBV 中 url调用StudentView.as_view()方法 而源码中as_view()方法返回的是view()方法 view方法最终返回的是dispatch方法
# 因此 访问url后 实际上直接执行的是视图的dispatch()方法
# dispatch方法内部则是基于反射,来执行GET、POST等方法 这就是url能找到CBV的原理 本质上就是基于反射实现的
class MyBaseView(object):
# 根据dispatch方法进行反射 分发到对应方法
def dispatch(self, request, *args, **kwargs):
print('bofore')
ret = super(MyBaseView, self).dispatch(request, *args, **kwargs)
# 调用父类dispatch方法 父类中的dispatch其实就是反射到get等方法
'''
def dispatch(self, request, *args, **kwargs):
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
return handler(request, *args, **kwargs)
'''
print('after')
return ret
# url -> 进入视图后 会调用dispatch方法 如果当前类中没有 回去父类中调用 调用从左到右 按照mro顺序执行
# MyBaseView 在左 所以会先去MyBaseView中寻找dispatch方法,进行反射,
class StudentView(MyBaseView, View):
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
def put(self, request):
return HttpResponse('put')
def delete(self, request):
return HttpResponse('delete')
列表生成式
class Foo(object):
pass
class Bar(object):
pass
v = [item() for item in [Foo, Bar]] # 对象列表[Foo(), Bar()]
# 等价于
v = []
for i in [Foo, Bar]:
obj = i()
v.append(obj)
# v 是一个对象列表
面向对象
封装
-
对同一类方法封装到类中
class File(object): # 对文件的curd方法 pass class DB(object): # 对数据库的curd方法 pass
-
将数据封装到对象中
class File(object):
def __init__(self,a1,a2):
self.a1 = a1
self.xxx = a2
pass
obj1 = File(123,123)
# obj具有的属性是 a1 = 123 和 xxx = 123 每个对象都有各自的属性
obj2 = File(123,245)
面试题
0.什么是restful规范
restful规范 这种编程一般叫做面向资源编程, 它把网络上的一切视为资源, 而对这个资源可以CRUD
设置路由的时候最后一个单词一般是资源名词,对资源进行curd的时候根据method的不同进行操作
restful规范也建议数据传输使用json格式
首先介绍Restful是什么?是对接口的一个规范,用于程序和程序之间数据交换的默认的一套规则
Restful规范建议
1.建议用https代替http 以前可能数据传输过程中是明文,https数据传输是加密的,安全性更好
2.在url中要体现api,建议加上api标识
https://www.baidu.com/api/gen/article/5/ 正确 体现api标识
https://api.baidu.com/api/gen/article/5/ 这种涉及到了跨域
3.在URL中要体现版本 版本不一定非得写到url中 也可以写在请求头中 不过还是建议写在url中
https://127.0.0.1:8000/api/v1/userinfo/
https://127.0.0.1:8000/api/v2/userinfo/
4.一般情况下对于api接口,用名词不用动词 面向资源编程
https://127.0.0.1:8000/api/v1/userinfo/ # 建议
https://127.0.0.1:8000/api/v1/getinfo/ # 不建议使用动词
5.还是一种规范,不是必须遵循,只是一种建议,具体按照具体业务实现
6.如果有条件,在url后面传递
https://127.0.0.1:8000/api/v1/userinfo/?page=1&category=2
7.根据method不同,做不同的操作,get/post/put/patch/delete
1.Restful10条规范(面试回答)?
第一步 :整体说Restful规范是什么?
Restful规范是对接口的一个规范,用于程序和程序之间数据交换的默认的一套规则
第二步:详细说Restful建议
1. https代替http,保证数据传输更加安全
2. 在url中一般要体现api标识,这样一看到url就知道他是一个api
https://www.baidu.com/api/gen/article/5/ (建议使用这种,因为他不存在跨域问题)
https://api.baidu.com/api/gen/article/5/ 这种涉及到了跨域
3. 在接口中要体现版本
https://127.0.0.1:8000/api/v1/userinfo/ (建议使用这种,因为他不存在跨域问题)
注意:版本还可以放在请求头中
https://127.0.0.1:8000/api/userInfo/
accept:...
4. restful也称为面向资源编程,视网络上的一切都是资源,对资源可以进行操作,所以一般资源都用名词不用动词
https://127.0.0.1:8000/api/userInfo/
5. 如果要加上一定的筛选条件,可以在url后面添加
https://127.0.0.1:8000/api/userInfo/?page=1&category=2
6. 根据method的不同做不同的操作
7. 返回给用户状态码
- 200, 成功
- 300, 301永久,302临时
- 400, 403拒绝访问, 404 找不到页面
- 500, 服务端代码错误
很多公司会带上自己定义的状态码
data_info = {
'code': 40003,
'data': random_string
}
8. 返回值,针对不同的操作,服务器返回结果应遵循
get /userinfo/ 返回资源对象的列表(数组)
get /userinfo/1/ 返回单个资源对象
post /userinfo/ 返回新生成的资源对象
put /userinfo/1/ 返回完整的资源对象
patch /userinfo/1/ 返回完整的资源对象
delete /userinfo/1/ 返回一个空文档
9. 操作异常时候,要返回错误信息
{
"error": "用户认证失败"
}
10. 对于下一个请求,返回一些接口 Hypermedia API 基本不遵循
{
'id':1,
'name':'xiaoxia',
'age': 22,
'depart': "https://127.0.0.1:8000/api/depart/"
}
2.什么是drf
drf是基于django的一个框架,他可以帮助我们快速开发遵循Restful规范的接口,他本质上是django的一个app
他提供了一些方便的组件,
视图
比如他给我们提供了一大堆的视图, APIView是最底部的,还有ListAPIView,CreateAPIView等等
这些只能完成对表的最基本的增删改查,如果业务比较复杂,需要重写
解析器
帮我重新封装了新的取值方法
request.data 取出请求体中数据
request.query_params url中的数据等
序列化
帮助我们快速序列化以及数据校验
分页
生命周期
请求到来之后,先执行视图的dispatch方法
1.视图
2.版本处理
3.认证
4.权限
5.节流(频率限制)
6.解析器
7.序列化
8.筛选器
9.分页
10.渲染
3.django中间件方法
- process_request
- process_view
- process_response
- process_exception
- process_render_template
4.使用中间件做过什么
-
权限
-
用户登录认证
-
django的csrf中间件是怎么实现的?
process_view方法中 1、检查视图是否被装饰器@csrf_exempt装饰(免除csrf认证) 2、去请求体或者cookie中获取token
情况1: 全站都使用csrf认证 再不需要的视图函数使用装饰器@csrf_exempt免除csrf认证
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # 全站使用csrf认证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt # 免除当前视图函数csrf认证
def user(request):
if request.method == 'GET':
return HttpResponse('ok')
情况2:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware', # 全站都不使用csrf认证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_protect # 给当前视图函数添加csrf认证
def user1(request):
if request.method == 'GET':
return HttpResponse('ok')
5.CBV小知识
在使用method_decorator(csrf_exempt)方法给类视图中的某些方法如post/put 免除csrf认证需要装饰在dispatch方法之上
from django.shortcuts import render, HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
# Create your views here.
class StudentView(View):
# 装饰器需要加载dispatch方法上
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
ret = super(StudentView, self).dispatch(request, *args, **kwargs)
return ret
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
def put(self, request):
return HttpResponse('put')
def delete(self, request):
return HttpResponse('delete')
# 或者 装饰器加在类上
@method_decorator(csrf_exempt, name='dispatch')
class StudentView(View):
def dispatch(self, request, *args, **kwargs):
ret = super(StudentView, self).dispatch(request, *args, **kwargs)
return ret
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
def put(self, request):
return HttpResponse('put')
def delete(self, request):
return HttpResponse('delete')
6.谈谈面向对象
-
封装
-
1 自定义分页器
2 最近看了看rest framework源码,提现封装概念的有一个request 框架对request进行了封装
他被重新封装成了新的包含了原生request和认证对象列表的request对象
-
-
继承
-
APIView
自定义类继承了APIView,APIView继承View 就是将公共的部分提取到一个基类中,然后使用继承快速完成或者加工功能
Form表单继承了BaseForm,帮助实现了is_valid方法
-
-
多态
-
说出鸭子模型即可
何为鸭子模型,只要一个东西会鸭子嘎嘎叫,那么就可以认为他是鸭子
来了个鹦鹉会嘎嘎叫,那么也可以将他叫为鸭子
-
例子
-
class WX: def send(): pass class Email: def send(): pass class Msg: def send(): pass def func(arg): arg.send() obj = Email() func(obj)
-
7.Django生命周期
- wsgi
- wsgi,是一个协议
- wsgiref,实现了wsgi协议的一个模块。模块的本质:socket服务端 测试用 性能低(Django)
- werkzeug,是实现了wsgi协议的一个模块。模块的本质:socket服务端 (Flask框架)
- uwsgi(最后部署使用)
- 中间件
- 穿过所有的中间件,做路由匹配找到视图函数,视图函数通过ORM找到数据,通过模板渲染返回给用户
8.中间件&装饰器
- 例子:用户登录,最开始版本是在视图函数上+装饰器
- 如果每个视图都需要登录校验,就得给每个函数上+装饰器,麻烦,这时候使用中间件非常合适
- 中间件适合对所有视图函数统一进行操作
- 对单个使用装饰器来做
- 应用场景
- 基于角色的权限控制
- 用户认证
- csrf(说原理)
- session(说原理)
- 黑名单
- 日志记录
9.rest framework 框架原理
-
认证
-
请求进来 封装request ->initial-> perform_authentication -> request.user - > authenticate
-
-
权限
-
请求进来,initial -> check_permissions -> has_permission
-
-
节流
-
请求进来,initial -> check_throttles-> allow_request
-
10.什么jwt,优势是什么(面试题)
一般在前后端分离时,用于做用户登录认证所使用的一项技术
jwt的实现原理:
- 用户登录成功后,会给前端返回一个token
- token是由.分割的3段组成
- 第一段:类型和算法信息 base64加密
- 第二段:用户信息(非敏感数据),超时时间 base64加密
- 第三段:hs256(前两段拼接后)加密 + base64url加密
- 以后前端再发来信息时,
- 超时验证
- token合法性检验
jwt优势:
- token只在前端保存 后端只负责校验
- 内部集成超时时间,后端可以根据超时时间进行校验是否超时。
- 内部存在hash256加密,所有用户不能够修改token,只要修改,认证失败
总结
- 本质,基于反射实现的根据不同请求方式进行不同函数调用
- 流程:路由 -> view -> dispatch(反射)
- 取消csrf认证(需要使用method_decorator并且要在dispatch方法上进行装饰)
扩展:
-
csrf
- 基于中间件的process_view方法
- 装饰器给单独的函数进行设置(@csrf_exempt/@csrf_protect)
-
中间件
-
csrf原理
-
Restful 10条规范
-
面向对象
-
django请求声明周期
-
django请求声明周期(包含rest framework框架)PS:dispatch
-
rest framework 认证流程(封装request.user)
-
rest framework 权限流程
-
rest framework 节流流程
代码:
-
认证demo
-
权限demo(用户类型不同,权限不同)
-
节流demo(匿名用户,登录用户,对于不同用户访问限制不同)
三组件组合使用
Django rest framework 源码剖析
# 下载 django rest framework
pip install django rest framework
drf简单实用例子
原生django序列化操作
# urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('category/', views.CategoryView.as_view()),
re_path(r'^category/(?P<pk>\d+)/$', views.CategoryView.as_view())
]
# views.py
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from . import models
# model_to_dict 可以将obj转换为dict类型
from django.forms.models import model_to_dict
# Create your views here.
class CategoryView(APIView):
def get(self, request, *args, **kwargs):
# get传参从kwarg中取
pk = kwargs.get('pk')
if not pk:
queryset = models.Category.objects.all()
# <QuerySet [<Category: Category object (2)>, <Category: Category object (3)>]>
print(queryset)
data_li = list(queryset)
print(data_li)
# <QuerySet [{'id': 2, 'name': 'sbn'}, {'id': 3, 'name': '其他'}]>
queryset = models.Category.objects.all().values('id', 'name')
print(queryset)
data_list = list(queryset)
print(data_list)
# Response 会自动帮我们转换为json类型
# 但是只能转换 字典 列表 类型的
return Response(data_list)
else:
cat_obj = models.Category.objects.filter(id=pk).first()
print(type(cat_obj))
# model_to_dict 对象转换为字典
data = model_to_dict(cat_obj)
print(type(data))
return Response(data)
def post(self, request, *args, **kwargs):
models.Category.objects.create(**request.data)
return Response('成功')
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Category.objects.filter(id=pk).delete()
return Response('删除成功')
def put(self, request, *args, **kwargs):
'''更新'''
pk = kwargs.get('pk')
models.Category.objects.filter(id=pk).update(**request.data)
return Response('更新成功')
drf序列化进行curd
class CategroySerializer(serializers.ModelSerializer):
class Meta:
model = models.Category
# fields = "__all__"
fields = ['id', 'name']
class NewCategoryView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
queryset = models.Category.objects.all()
ser = CategroySerializer(instance=queryset, many=True)
return Response(ser.data)
else:
cat_obj = models.Category.objects.filter(id=pk).first()
ser = CategroySerializer(instance=cat_obj, many=False)
return Response(ser.data)
def post(self, request, *args, **kwargs):
print(request.data)
ser = CategroySerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
# 一定.first()拿到对象 否则拿到的是queryset
cat_obj = models.Category.objects.filter(id=pk).first()
ser = CategroySerializer(instance=cat_obj, data=request.data)
if not ser.is_valid():
return Response(ser.errors)
ser.save()
return Response(ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Category.objects.filter(id=pk).delete()
return Response('删除成功')
所有组件
- 认证(重要)
- 权限(重要)
- 节流(重要)
- 版本
- 解析器
- 序列化器(重要)
- 对请求数据进行校验
- QuerySet进行序列化
- 分页
- 路由
- 视图
- 渲染器
- django组件:contenttype
1.认证
-
认证引入
- 问题1:有些API需要用户登录之后才能访问,有些无需登录才能访问
- 基本认证组件使用
- 解决
- 创建两张表(UserInfo,UserToken)
- 用户登录(返回token并且保存到数据库)
- 解决
- 认证原理流程图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJRiyo1e-1646892881897)(assets/image-20220207113146314-16442067250752.png)]
- 在看一遍源码
- 局部视图使用 & 全局视图使用
- 匿名 request.user = None
- 内置认证类
- 认证类,必须继承 from rest_framework.authentication import BaseAuthentication
- 其他认证类:BasicAuthentication
-
认证流程
-
仅使用
# ----------------------------- rest framework -------------------------------- import json from rest_framework.views import APIView from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from rest_framework.request import Request # 首先自定义认证类 也可以直接使用rest framework提供的认证类 # 所有认证类中必须有 这两个方法 authenticate中提供认证方法 authenticate_header class Myauthentication(object): def authenticate(self, request): token = request._request.GET.get('token') # 实际应该去数据库中取出数据 然后校验 if not token: raise AuthenticationFailed('用户认证失败') return ('xiaoxia', None) def authenticate_header(self, val): pass class DogView(APIView): # 使用的用户认证类必须写入authentication_classes列表中 ,rest framework会自动执行认证类校验 authentication_classes = [Myauthentication, ] def get(self, request, *args, **kwargs): print(request.user) # request.user 就是认证通过后的对象 xiaoxia ret = { 'code': 1000, 'msg': 'xxx' } return HttpResponse(json.dumps(ret), status=201) def post(self, request, *args, **kwargs): return HttpResponse('创建dog') def put(self, request, *args, **kwargs): return HttpResponse('更新dog') def delete(self, request, *args, **kwargs): return HttpResponse('删除dog')
-
认证源码执行流程
url - > view -> dispatch()方法进行反射
dispatch方法 首先对request进行加工 封装成了 Request() 包含了 原生request 以及 认证类对象
request._request 取出原生request
request.authenticators 取出认证类对象
# 1、dispatch方法 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进行加工(丰富了一些功能) # Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(), # [Foo(), Bar() 对象]negotiator=self.get_content_negotiator(),parser_context=parser_context) # 1. 封装Request # request(原生request, [BaseAuthentication对象,]) request = self.initialize_request(request, *args, **kwargs) # 获取原生request, request._request # 获取认证类的对象, request.authenticators self.request = request self.headers = self.default_response_headers # deprecate? try: # 2. 认证 - ↓ self.initial(request, *args, **kwargs) # 进入initial方法 # 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 # 2、initial方法 def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # 3. 实现认证 # 用户认证 self.perform_authentication(request) # 进入perform_authentication方法 # 权限控制 self.check_permissions(request) # 节流控制(访问频率) self.check_throttles(request) # 3、perform_authentication方法 def perform_authentication(self, request): request.user # 返回request.user方法 进入user方法中 # 4、user @property # 魔法方法 def user(self): if not hasattr(self, '_user'): with wrap_attributeerrors(): # 4.获取用户对象 进行一步步认证 self._authenticate() # 进入_authenticate方法 return self._user # 5、_authenticate方法 def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # [BaseAuthentication, ] 认证类对象列表 for authenticator in self.authenticators: try: # 执行认证类的authenticate方法 # 1 如果这个方法抛出异常 self._not_authenticated() 执行 并且在往上级抛出 # 2 有返回值 必须是元组 (request.user, request.auth) # 3 返回None 当前认证不处理 交给下一个认证处理 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
-
1.1 认证demo
模型类
# models.py
from django.db import models
# Create your models here.
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=256)
# 用户token表
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
视图函数
# views.py
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from rest_framework.views import APIView
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from api import models
# Create your views here.
def md5(user):
import hashlib
import time
ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding='utf-8'))
m.update(bytes(ctime, encoding='utf-8'))
return m.hexdigest()
# 模拟数据库中的数据
ORDER_DICT = {
1: {
'name':'媳妇',
'age': 12,
'sex': '男',
'content':'宪法关系'
},
2: {
'name': '老狗',
'age': 15,
'sex': '女',
'content': '主奴'
}
}
# 路由 path(r'api/v1/auth/', views.AuthView.as_view())
class AuthView(APIView):
'''用于用户登录认证'''
def post(self, request, *args, **kwargs):
ret = {'code':1000, 'msg': None}
try:
user = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用户名或密码错误'
# 为用户创建token
token = md5(user)
# 存在就创建 不存在就更新
models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
ret['token'] = token
except Exception as e:
ret['code'] = 1002
ret['msg'] = '请求异常'
return JsonResponse(ret)
class Authtication(object):
'''自定义认证类'''
def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed('用户认证失败')
# 在drf内部 会将这两个字段赋值给request,以供后续使用
# 分别通过 request.user request.auth 调用拿到两个模型类对象
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
# 路由 path(r'api/v1/order/', views.OrderView.as_view())
class OrderView(APIView):
'''订单相关业务,同样需要认证用户才能访问'''
# 调用认证类 局部使用 也可以全局使用 在setting文件中配置全局使用
authentication_classes = [Authtication, ]
def get(self, request, *args, **kwargs):
# 可以在每个视图中添加token判断 但是drf为我们做好了认证方法
# token = request.GET.get('token')
# if not token:
# return HttpResponse('用户未登录')
print(request.user.username)
print(request.auth.token)
ret = {'code': 1000, 'msg': None}
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
1.2 认证梳理
-
使用
-
创建认证类,继承BaseAuthentication, 所有认证类中必须有 这两个方法 authenticate中提供认证方法 authenticate_header
from rest_framework.exceptions import AuthenticationFailed from rest_framework.authentication import BaseAuthentication from api import models # 推荐使用继承BaseAuthentication类 class Authtication(BaseAuthentication): '''自定义认证类''' def authenticate(self, request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise AuthenticationFailed('用户认证失败') # 在drf内部 会将这两个字段赋值给request,以供后续使用 return (token_obj.user, token_obj) def authenticate_header(self, request): pass
-
返回值有三种
- None 该认证类不管了 交给下一个认证类执行。
- raise AuthenticationFailed(‘用户认证失败’) # from rest_framework.exceptions import AuthenticationFailed
- 元组(元素1, 元素2)# 元素1 赋值给 request.user 元素2 赋值给 request.auth
-
局部使用
class DogView(APIView): # 使用的用户认证类必须写入authentication_classes列表中 ,rest framework会自动执行认证类校验 # 局部使用 authentication_classes = [Myauthentication, ] def get(self, request, *args, **kwargs): print(request.user) # request.user 就是认证通过后的对象 xiaoxia ret = { 'code': 1000, 'msg': 'xxx' } return HttpResponse(json.dumps(ret), status=201)
-
全局使用(注意差别:一个引入类,一个引入路径)
# rest framework 全局配置 # rest framework中会自动加载 setting中的REST_FRAMEWORK配置项 这是一个字典 # 在这里面可以写上认证,权限,节流等所有配置项 REST_FRAMEWORK = { # 这里的认证类写的是路径 全局使用认证类 'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.Authtication'], # 这里设置 未通过认证用户的 user名称 默认是 AnonymousUser # 'UNAUTHENTICATED_USER': lambda :'匿名用户' 推荐使用None 'UNAUTHENTICATED_USER': None, # 匿名 未登录 request.user = None 'UNAUTHENTICATED_TOKEN': None, # 匿名 request.auth = None }
-
源码流程
-dispatch -封装request -获取定义的认证类(全局/局部),通过列表生成式创建对象。 - initial -perform_authentication request.user(内部循环认证)
-
1.3认证总结
当用户发来请求时, 找到所有的认证类并实例化成为对象列表,然后将对象列表封装到新的request对象中
以后在视图中调用request.user
在内部会循环认证的对象列表,并执行每个对象的authenticate方法,该方法用于认证
它会返回两个值分别赋值给request.user/request.auth
1.4 jwt原理以及案例
用于前后端分离,实现用户登录相关
一般用户认证有2种方式
-
token
用户登录成功后,生成一个随机字符串,自己保留一份,返回给前端一份 以后前端再来请求,需要携带token字符串 后端对字符串校验。
-
jwt
用户登录成功之后,生成一个随机字符串返回给前端 - 生成随机字符串 {"typ":"jwt", "hs":"..."} {id:1, username:'xx', 'exp':10} saoaohsduo123iho1asdhouasohd21.asohgoeji911h29821hoasfh0asdasssd.08qweu0qhfvwhasduha123sfaash - 类型信息通过base64加密 - 数据通过base64加密 - 两个密文拼接起来在通过h256加密+加盐 - 给前端返回 saoaohsduo123iho1asdhouasohd21.asohgoeji911h29821hoasfh0asdasssd.08qweu0qhfvwhasduha123sfaash - 前端获取随机字符串之后,保留起来。 - 以后再发来请求时,携带字符串 - 后端接收到之后, - 1.先做时间判断 - 2.字符串合法性校验
1.5 jwt使用
pip install djangorestframework-jwt
2.权限
-
权限引入
-
问题:不同的视图不同的权限才可以访问
-
基本使用
class MyPermission(object): '''自定义权限类''' def has_permission(self, request, view): if request.user.user_type != 3: return False return True class OrderView(APIView): '''订单相关业务(只有SVIP可以访问)''' # 这里写入权限类 permission_classes = [MyPermission, ] def get(self, request, *args, **kwargs): # 可以在每个视图中添加token判断 但是drf为我们做好了认证方法 # token = request.GET.get('token') # if not token: # return HttpResponse('用户未登录') print(request.user.username) print(request.auth.token) ret = {'code': 1000, 'msg': None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret)
-
权限源码流程(与认证特别相似)
-url -> view -> dispatch(反射) -> initial -> check_permissions # 1 - dispatch 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进行加工(丰富了一些功能) # Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(), # [Foo(), Bar() 对象]negotiator=self.get_content_negotiator(),parser_context=parser_context) # request = Request # request(原生request, [BaseAuthentication对象,]) request = self.initialize_request(request, *args, **kwargs) # 获取原生request, request._request # 获取认证类的对象, request.authenticators # 封装request 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 # 2 initial def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # 认证 self.perform_authentication(request) # 权限 self.check_permissions(request) # 节流(访问频率) self.check_throttles(request) # 3 check_permissions def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ # 去取出每一个权限类 通过权限啥都不做,不通过就返回message for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None), code=getattr(permission, 'code', None) )
-
2.1 权限梳理
-
使用
-
创建权限类,必须继承BasePermission, 必须实现has_permission方法 可以在类中定义message属性作为权限不通过时的返回内容
from rest_framework.permissions import BasePermission class SSVIPPermission(BasePermission): '''自定义权限类''' # 没有权限时,返回的信息 message = '只有SSVIP才能访问' def has_permission(self, request, view): if request.user.user_type != 3: return False return True
-
返回值有两种
- True 有权访问
- FALSE 无权访问
-
局部使用
class OrderView(APIView): '''订单相关业务(只有SVIP可以访问)''' # 这里写入权限类 局部使用 permission_classes = [SSVIPPermission] def get(self, request, *args, **kwargs): # 可以在每个视图中添加token判断 但是drf为我们做好了认证方法 # token = request.GET.get('token') # if not token: # return HttpResponse('用户未登录') print(request.user.username) print(request.auth.token) ret = {'code': 1000, 'msg': None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret)
-
全局使用(注意差别:一个引入类,一个引入路径)
REST_FRAMEWORK = { # 认证类全局配置 # 这里的认证类写的是路径 全局使用认证类 'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.Authtication'], # 这里设置 未通过认证用户的 user名称 默认是 AnonymousUser # 'UNAUTHENTICATED_USER': lambda :'匿名用户' 推荐使用None 'UNAUTHENTICATED_USER': None, # 匿名 未登录 request.user = None 'UNAUTHENTICATED_TOKEN': None, # 匿名 request.auth = None # 权限类全局配置 'DEFAULT_PERMISSION_CLASSES': ['api.utils.permission.SSVIPPermission'] }
-
源码流程
-dispatch -封装request -获取自定义权限类(全局/局部), 通过列表生成式创建对象 - initial - check_permissions(找到权限类) - has_permission(调用权限方法) - 当访问单个数据 RetrieveAPIView时,会调用权限验证类的第二个方法has_object_permission
-
3.节流(访问频率控制)
-
节流引入
-
问题:控制访问频率
-
基本使用
# 自定义节流类实现原理 class VisitThrottle(object): def __init__(self): self.history = None def allow_request(self, request, view): # return True 表示可以继续访问 return False 表示访问频率太高,被限制 # 1. 获取用户IP remote_addr = request.META.get('REMOTE_ADDR') import time ctime = time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime] return True history = VISIT_RECORD.get(remote_addr) self.history = history while history and history[-1] < ctime - 60: history.pop() if len(history) < 3: history.insert(0, ctime) return True def wait(self): '''还需要等多少秒才可以继续访问''' ctime = time.time() return 60 - (ctime - self.history[-1]) # 应用 class AuthView(APIView): '''用于用户登录认证''' # 全局配置了认证类 这里定义空列表就能解除认证类 authentication_classes = [] # 局部配置权限类 permission_classes = [] # 局部配置节流类 throttle_classes = [VisitThrottle] def post(self, request, *args, **kwargs): ret = {'code':1000, 'msg': None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user, password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' # 为用户创建token token = md5(user) # 存在就创建 不存在就更新 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求异常' return JsonResponse(ret)
-
节流源码流程(其实认证、权限、节流流程机会一致,看懂一个全都会了)
-url -> view -> dispatch(反射) -> initial -> check_throttles 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进行加工(丰富了一些功能) # Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(), # [Foo(), Bar() 对象]negotiator=self.get_content_negotiator(),parser_context=parser_context) # request = Request # request(原生request, [BaseAuthentication对象,]) request = self.initialize_request(request, *args, **kwargs) # 获取原生request, request._request # 获取认证类的对象, request.authenticators # 封装request 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 initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # 认证 self.perform_authentication(request) # 权限 self.check_permissions(request) # 节流(访问频率) self.check_throttles(request) def check_throttles(self, request): """ Check if request should be throttled. Raises an appropriate exception if the request is throttled. """ throttle_durations = [] for throttle in self.get_throttles(): if not throttle.allow_request(request, self): throttle_durations.append(throttle.wait()) if throttle_durations: # Filter out `None` values which may happen in case of config / rate # changes, see #1438 durations = [ duration for duration in throttle_durations if duration is not None ] duration = max(durations, default=None) self.throttled(request, duration)
-
3.1 节流梳理
-
使用
-
创建节流类,继承BaseThrottle类, 实现allow_request 和 wait方法
-
节流类,继承SimpleRateThrottle, 继承后只需要实现 get_cache_key 和 scope(配置文件中的key)
import time from rest_framework.throttling import BaseThrottle, SimpleRateThrottle VISIT_RECORD = {} # 两种方法 1 继承BaseThrottle class VisitThrottle1(BaseThrottle): def __init__(self): self.history = None def allow_request(self, request, view): # return True 表示可以继续访问 return False 表示访问频率太高,被限制 # 1. 获取用户IP remote_addr = request.META.get('REMOTE_ADDR') ctime = time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime] return True history = VISIT_RECORD.get(remote_addr) self.history = history while history and history[-1] < ctime - 60: history.pop() if len(history) < 3: history.insert(0, ctime) return True def wait(self): '''还需要等多少秒才可以继续访问''' ctime = time.time() return 60 - (ctime - self.history[-1]) # 继承SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): '''节流类''' scope = 'xiaoxia' # 按照IP进行节流 def get_cache_key(self, request, view): return self.get_ident(request)
-
局部使用
class VisitThrottle(SimpleRateThrottle): '''节流类''' scope = 'xiaoxia' # 按照IP进行节流 def get_cache_key(self, request, view): return self.get_ident(request) class AuthView(APIView): '''用于用户登录认证''' # 全局配置了认证类 这里定义空列表就能解除认证类 authentication_classes = [] permission_classes = [] # 局部使用 节流类 throttle_classes = [VisitThrottle] def post(self, request, *args, **kwargs): ret = {'code':1000, 'msg': None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user, password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = '用户名或密码错误' # 为用户创建token token = md5(user) # 存在就创建 不存在就更新 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求异常' return JsonResponse(ret)
-
全局使用
# rest framework 全局配置 # rest framework中会自动加载 setting中的REST_FRAMEWORK配置项 这是一个字典 # 在这里面可以写上认证,权限,节流等所有配置项 REST_FRAMEWORK = { # 认证类全局配置 # 这里的认证类写的是路径 全局使用认证类 'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.Authtication'], # 这里设置 未通过认证用户的 user名称 默认是 AnonymousUser # 'UNAUTHENTICATED_USER': lambda :'匿名用户' 推荐使用None 'UNAUTHENTICATED_USER': None, # 匿名 未登录 request.user = None 'UNAUTHENTICATED_TOKEN': None, # 匿名 request.auth = None # 权限类全局配置 'DEFAULT_PERMISSION_CLASSES': ['api.utils.permission.SSVIPPermission'], # 节流类全局配置 'DEFAULT_THROTTLE_CLASSES': ['api.utils.throttle.VisitThrottle'], 'DEFAULT_THROTTLE_RATES': { 'xiaoxia': '3/m' } }
-
源码流程
-
-dispatch -封装request -获取自定义权限类(全局/局部), 通过列表生成式创建对象 - initial - check_throttles(找到节流类) - allow_request(调用节流方法)
-
-
4.3节流面试回答
-
如何实现访问频率限制
- 匿名用户,用IP作为用户唯一标识,但是如果用户换了代理IP, 无法做到真正的限制 - 登录用户,用用户名或者ID做标识 具体实现: 在django的缓存中维护了有一个字典 = { throttle_anon_1.1.1.1: [1010101,] 1.1.1.2: [123131232, 213213213] # 列表中存的是访问ip的时间戳 } 限制:60s访问3次 来访问时: 1.获取当前时间 100121280 2.100121280 - 60 = 1001121220, 小于1001121220的所有记录删除 3.判断1min中内已经访问了多少次? 4 4.无法访问 停一会 来访问时: 1.获取当前时间 100121340 2.100121340 - 60 = 1001121220, 小于1001121220的所有记录删除 3.判断1min中内已经访问了多少次? 0 4.可以访问
4.版本
4.1基本使用
-
URL通过GET传参
-
自定义版本类
# http://127.0.0.1:8000/api/users/?version=v1 # url path('users/', views.UserView.as_view()), class ParamVersion(object): '''版本类''' def determine_version(self, request, *args, **kwargs): version = request.query_params.get('version') return version # 局部使用 class UserView(APIView): # 这里配置局部版本类 versioning_class = ParamVersion def get(self, request, *args, **kwargs): print(request.version) return HttpResponse('用户列表')
-
-
在URL中传参(推荐使用)
-
使用 URLPathVersioning
-
全局配置版本类
# urls.py # http://127.0.0.1:8000/api/v1/users/ urlpatterns = [ re_path(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view()), ] # 全局配置 # REST_FRAMEWORK REST_FRAMEWORK = { # 全局版本配置 # 版本类路径 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 默认版本 'DEFAULT_VERSION': 'v1', # 允许的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # url参数 'VERSION_PARAM': 'version' }
-
源码流程
url -> views -> dispatch() -> initial -> determine_version -> return request.version, request.versioning.scheme class UserView(APIView): def get(self, request, *args, **kwargs): self.dispatch() # 获取版本 print(request.version) # 获取版本对象 URLPathVersioning() print(request.versioning_scheme) return HttpResponse('用户列表')
-
4.2 版本梳理
总结
-
使用
-
配置文件
# REST_FRAMEWORK REST_FRAMEWORK = { # 全局版本配置 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version' }
-
路由系统
# 路由分发 urls.py from django.urls import path, include urlpatterns = [ path('api/', include('api.urls')) ] # api.urls.py urlpatterns = [ # 路由得这么写 re_path(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='user'), ]
-
视图
class UserView(APIView): def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本对象 URLPathVersioning() print(request.versioning_scheme) # 反向生成url(rest framework) url = request.versioning_scheme.reverse(viewname='user', request=request) print(url) # 反向生成url(django) url = reverse(viewname='user', kwargs={'version':2}) print(url) return HttpResponse('用户列表')
-
5.解析器
作用
对用户请求体中的数据进行解析,依靠请求头中的Content-Type对请求体中的数据进行解析,解析到request.data中去
前戏(DJango解析器)
django:request.POST / request.body
-
发送POST请求过来后,request.body中一定有值,但是request.POST中不一定有值
-
其实request.POST中的数据就是从request.body转换过来的,只要符合一定的请求条件,request.POST中就会有值
-
请求头要求
- 如果请求头中的 Content-Type:application/x-www-form-urlencoded, request.POST才会有值(去request.body中解析数据)
-
数据格式要求:
- name = ‘xiaoxia’ & age = 18 & gender = ‘男’
-
只要两个要求都满足的时候,request.POST中才会有值
-
例子
-
form表单提交,默认转换为这种格式 name = ‘xiaoxia’ & age = 18 & gender = ‘男’ POST中有值
-
ajax提交
-
情况1
# body有值 POS有值 $.ajax({ url;..., type:POST, data:[name:xxx,age:12] # 内部自动转换为 name='xiaoxia'&age=18&gender = '男' })
-
情况2
# body有值 POST无值 $.ajax({ url;..., type:POST, headers:['Content-Type':'application/json'] data:[name:xxx,age:12] # 内部自动转换为 name='xiaoxia'&age=18&gender = '男' })
-
情况3
# body有值 POST无值 $.ajax({ url;..., type:POST, data:JSON.stringfy([name:xxx,age:12]) # {"name":"xiaoxia", "age": "16"} })
-
-
-
restframework解析器
解析器梳理
-
局部使用
# urls.py urlpatterns = [ re_path(r'^(?P<version>[v1|v2]+)/parser/$', views.ParserView.as_view()), ] # views.py from rest_framework.parsers import JSONParser, FormParser class ParserView(APIView): # 局部配置解析器列表 parser_classes = [JSONParser, FormParser] ''' JSONParser:表示只能解析Content-Type:application-json头 FormParser:表示只能解析Content-Type:x-www-form-urlencoded头 ''' def post(self, request, *args, **kwargs): # 获取解析之后的结果 根据解析器不同 结果不同 ''' 其实是request.data来触发解析器的 光配置解析器,不调用request.data是不会触发解析器的 1.获取用户请求 2.获取用户请求体 3.根据用户请求头和 parser_classes = [JSONParser, FormParser]中支持的请求头进行比较 4.JSONParser对象去解析 5.request.data 获取解析后的数据 ''' print(request.data) return HttpResponse('ParserView')
-
全局使用(推荐使用,配置好即可)
# REST_FRAMEWORK REST_FRAMEWORK = { # 全局版本配置 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version', # 全局解析器配置 'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser'] }
-
源码流程 & 本质 可能会问到的面试题
-
本质:根据请求头Content-Type的不同,调用不同的解析器进行处理,解析就是将请求体中的数据转化为不同格式的数据
-
请求头
Accept:浏览器能够处理的内容类型 Accept-Charset:浏览器能够显示的字符集 Accept-Encoding:浏览器能够处理的压缩编码 Accept-Language:浏览器当前设置的语言 Connection:浏览器与服务器之间连接的类型 Cookie:当前页面设置的任何Cookie Host:发出请求的页面所在的域 Referer:发出请求的页面的URL User-Agent:浏览器的用户代理字符串 Content-Type: 发送请求的数据格式
-
状态码
200 - 请求成功 301 - 资源(网页等)被永久转移到其它URL 404 - 请求的资源(网页等)不存在 500 - 内部服务器错误
-
请求方法:GET/POST/PUT/PATCH不常用/DELETE/等
-
-
源码流程
- dispatch:request封装 原生request、认证对象、解析器对象
- 然后通过一些认证、权限、节流方法之后,通过反射找到对应请求的GET、POST方法
- request.data 执行后会重新找到对应解析器进行解析处理
-
6.序列化
序列化器功能:
- 数据校验 (表单校验)
- 序列化,对对象(obj)或者对象列表(queryset)进行序列化操作
####基本使用
import json
from rest_framework import serializers
# Role表
class Role(models.Model):
title = models.CharField(max_length=32)
# 自定义序列化器
class RolesSerializers(serializers.Serializer):
# 字段对应数据库中的字段
id = serializers.IntegerField()
title = serializers.CharField()
# 视图函数
class RolesView(APIView):
def get(self, request, *args, **kwargs):
# 方式1
# roles = models.Role.objects.all().values('id', 'title')
# roles = list(roles)
# ret = json.dumps(roles, ensure_ascii=False)
# 方式2:序列化器 对于 [obj, obj, obj,]
# roles = models.Role.objects.all()
# ser = RolesSerializers(instance=roles, many=True)
# print(ser.data)
# ret = json.dumps(ser.data, ensure_ascii=False)
# 对于单个对象
roles = models.Role.objects.all().first()
# 单个对象 Many=False
ser = RolesSerializers(instance=roles, many=False)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
序列化自定义字段
# models.py
from django.db import models
# Create your models here.
from django.db import models
# Create your models here.
class UserGroup(models.Model):
title = models.CharField(max_length=32)
class UserInfo(models.Model):
user_type_choices = (
(1, '普通用户'),
(2, 'VIP'),
(3, 'SVIP')
)
# 默认取出的是 1,2,3 如果想要取出具体用户, 需要调用 get_user_type_display()方法
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=256)
group = models.ForeignKey('UserGroup', on_delete=models.CASCADE)
roles = models.ManyToManyField('Role')
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
class Role(models.Model):
title = models.CharField(max_length=32)
# 自定义序列化器(理解source和自定义字段方法)
class UserInfoSerializer(serializers.Serializer):
# xxx = serializers.CharField(source='get_user_type_display')
# source就是源,指向user_type 就是去指定数据库取user_type字段
# 它内部会判断是否是可执行的字段
# 它会判断是否是可执行对象,如果是可执行字段,那么自动执行 user_type()
# 如果不是可执行的,那么不加 user_type 这是source原理
# 只有choices 和 ForeignKey类型可以加 ManytoMany类型不能使用source
user_type = serializers.CharField(source='get_user_type_display')
username = serializers.CharField()
password = serializers.CharField()
gp = serializers.CharField(source='group.title')
# rls = serializers.CharField(source='roles.all')
# roles是manytomany字段 不能够直接使用source
# 因此使用这种方法
rls = serializers.SerializerMethodField() # 自定义显示
# 自定义get_字段名()方法 return什么 那么字段返回什么
def get_rls(self, row):
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id':item.id, 'title':item.title})
return ret
# views.py
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=users, many=True)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
序列化ModelSerializer
# 继承ModelSerializer
class UserInfoSerializer(serializers.ModelSerializer):
user_type = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField() # 自定义显示
# 元类
class Meta:
model = models.UserInfo
# fields = '__all__'
fields = ['id', 'username', 'password', 'user_type', 'group', 'rls']
# 自定义序列化方法
def get_rls(self, row):
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id':item.id, 'title':item.title})
return ret
# views.py
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
# 实例化序列化器类 将要序列化的数据传入 instance中 多个数据使用many=True
ser = UserInfoSerializer(instance=users, many=True)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
序列化案例:
# models.py
from django.db import models
# Create your models here.
class Category(models.Model):
'''文章分类'''
name = models.CharField(verbose_name='分类', max_length=32)
class Article(models.Model):
'''文章表'''
status_choices = (
(1, '发布'),
(2, '删除')
)
status = models.IntegerField(verbose_name='状态', choices=status_choices, default=1)
title = models.CharField(verbose_name='标题', max_length=32)
summary = models.CharField(verbose_name='简介', max_length=255)
content = models.TextField(verbose_name='文章内容')
category = models.ForeignKey(verbose_name='分类', to='Category', on_delete=models.CASCADE)
tag = models.ManyToManyField(verbose_name='标签', to='Tag', null=True, blank=True)
class Tag(models.Model):
'''标签'''
title = models.CharField(verbose_name='标签', max_length=32)
# 序列化器
class AritcleSerizlizer(serializers.ModelSerializer):
status_txt = serializers.CharField(source='get_status_display', required=False)
category_txt = serializers.CharField(source='category.name', required=False)
# 钩子字段 manytomany 只能使用depth 或者 SerializerMethodField来跨表查询
tag_txt = serializers.SerializerMethodField()
# 钩子函数
def get_tag_txt(self, obj):
# return [row.title for row in obj.tag.all()]
return [row for row in obj.tag.all().values('id', 'title')]
class Meta:
model = models.Article
# fields = "__all__"
# depth = 1
fields = "__all__"
# views.py
class AritcleView(APIView):
'''crud'''
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
queryset = models.Article.objects.all()
ser = AritcleSerizlizer(instance=queryset, many=True)
return Response(ser.data)
else:
art_obj = models.Article.objects.filter(id=pk).first()
ser = AritcleSerizlizer(instance=art_obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = AritcleSerizlizer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Article.objects.filter(id=pk).first()
ser = AritcleSerizlizer(instance=obj, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def patch(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Article.objects.filter(id=pk).first()
ser = AritcleSerizlizer(instance=obj, data=request.data, partial=True)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Article.objects.filter(id=pk).update(status=2)
return Response('删除成功')
序列化器部分总结
1.写类
-
继承 serializers.Serializer
class UserInfoSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField()
-
继承 serializers.ModelSerializer
# 根据model自动生成序列化字段 class UserInfoSerializer(serializers.ModelSerializer): user_type = serializers.CharField(source='get_user_type_display') rls = serializers.SerializerMethodField() # 自定义显示 class Meta: # 对应的model表 model = models.UserInfo # 对应的字段 # fields = '__all__' fields = ['id', 'username', 'password', 'user_type', 'group', 'rls']
2.自定义字段
# source会判断是否是可执行对象,如果是执行对象,直接执行get_user_type_display()
# 如果不是可执行对象,直接get_user_type_display
user_type = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField() # 自定义显示
# 自定义序列化方法
def get_rls(self, row):
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id':item.id, 'title':item.title})
return ret
3.自动序列化连表操作(牛逼)深度控制
# models.py
from django.db import models
# Create your models here.
from django.db import models
# Create your models here.
class UserGroup(models.Model):
title = models.CharField(max_length=32)
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=256)
group = models.ForeignKey('UserGroup', on_delete=models.CASCADE)
roles = models.ManyToManyField('Role')
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
# 自定义序列化器
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = '__all__'
# views.py
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=users, many=True)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
默认访问后数据为
[
{
"id": 1,
"user_type": 1,
"username": "小夏",
"password": "123",
"group": 1,
"roles": [
1,
2,
3
]
},
{
"id": 2,
"user_type": 2,
"username": "小盛",
"password": "123",
"group": 1,
"roles": [
1,
3
]
},
{
"id": 3,
"user_type": 1,
"username": "小宋",
"password": "123",
"group": 1,
"roles": [
1
]
}
]
roles和user_type这些显示的都是基层,没有往深入去取,但我们往往想要的是具体数据,就得使用想上面一样的自定义序列化方法或者别的方法。
-
这里提供更简单的方法 深度控制
-
使用depth可以控制查询连表的深度
class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = '__all__' # 深度控制 尽量不要超过3 建议 1-10 depth = 1 class UserInfoView(APIView): def get(self, request, *args, **kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users, many=True) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
加上depth=1 再次访问url获取数据为
[ { "id": 1, "user_type": 1, "username": "小夏", "password": "123", "group": { "id": 1, "title": "IT组" }, "roles": [ { "id": 1, "title": "学生" }, { "id": 2, "title": "老师" }, { "id": 3, "title": "护士" } ] }, { "id": 2, "user_type": 2, "username": "小盛", "password": "123", "group": { "id": 1, "title": "IT组" }, "roles": [ { "id": 1, "title": "学生" }, { "id": 3, "title": "护士" } ] }, { "id": 3, "user_type": 1, "username": "小宋", "password": "123", "group": { "id": 1, "title": "IT组" }, "roles": [ { "id": 1, "title": "学生" } ] } ]
跨表访问(深层访问)
# 文章表序列化器
class ArticleSerializer(serializers.ModelSerializer):
# 跨表访问
# 1 使用source
# 2 使用 SerializerMethodField() 定义钩子函数
# 3 depth 不推荐
category = serializers.CharField(source='category.name')
# 深入访问
status = serializers.CharField(source='get_status_display')
status_txt = serializers.SerializerMethodField()
# MethodField 钩子方法
x1 = serializers.SerializerMethodField()
class Meta:
model = models.Article
fields = ['id', 'title', 'summary', 'content', 'category', 'x1', 'status', 'status_txt']
# depth = 1 可以用 但是不推荐
# 定义钩子函数 get_字段名 obj是返回的每一行对象
def get_x1(self, obj):
return obj.category.name
def get_status_txt(self, obj):
return obj.get_status_display()
# 文章表curd
class Article(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
print(pk)
if not pk:
queryset = models.Article.objects.all()
# 序列化操作
ser = ArticleSerializer(instance=queryset, many=True)
return Response(ser.data)
else:
art_obj = models.Article.objects.filter(id=pk).first()
print(art_obj)
ser = ArticleSerializer(instance=art_obj, many=False)
return Response(ser.data)
def post(self, request, *args, **kwargs):
# 数据校验 表单验证
ser = ArticleSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
art_obj = models.Article.objects.filter(id=pk).first()
ser = ArticleSerializer(instance=art_obj, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Article.objects.filter(id=pk).delete()
return Response('删除成功')
4.给字段反向生成链接
class UserInfoSerializer(serializers.ModelSerializer):
# 给group反向生成链接 固定用法 serializers.HyperlinkedIdentityField
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
class Meta:
model = models.UserInfo
fields = ['id', 'username', 'password', 'group']
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
# 如果使用HyperlinkedIdentityField 实例化序列化器时候需要多加 context={'request': request}参数
ser = UserInfoSerializer(instance=users, many=True, context={'request': request})
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
返回数据格式效果
[
{
"id": 1,
"username": "小夏",
"password": "123",
"group": "http://127.0.0.1:8000/api/v1/group/1"
},
{
"id": 2,
"username": "小盛",
"password": "123",
"group": "http://127.0.0.1:8000/api/v1/group/1"
},
{
"id": 3,
"username": "小宋",
"password": "123",
"group": "http://127.0.0.1:8000/api/v1/group/1"
}
]
5.请求数据校验
使用序列化器进行数据校验
基本使用
# 序列化器
class UserGroupSerializer(serializers.Serializer):
# 这里可以写自定义字段校验类
title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[])
# 同样可以写钩子函数进行数据校验
# 序列化校验方法 validate_字段名
def validate_title(self, val):
print(val, 1111)
return val
# views.py
class UserGroupView(APIView):
def post(self, request, *args, **kwargs):
# print(request.data) request.data就是解析之后的对象
# 实例化serializer对象
ser = UserGroupSerializer(data=request.data)
# 数据通过验证
if ser.is_valid():
print(ser.validated_data['title'])
else:
print(ser.errors)
return HttpResponse('提交数据')
7.分页
rest framework提供的三种分页方法(实质上是继承不同的类即可实现不同的方法,基本使用一致)
- 分页,看第n页,每页显示多少n条数据 继承 PageNumberPagination
- 分页,在n个位置,向后查n条数据 继承 LimitOffsetPagination
- 加密分页,上一页和下一页 继承 CursorPagination
分页器基本使用
1.使用rest framework提供好的
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response # 使用这个Response可以显示rest framework页面 看着比较舒服
# 分页序列化器
class Page1Serializer(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = '__all__'
# 分页
class Page1View(APIView):
def get(self, request, *args, **kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = PageNumberPagination()
# 在数据库中获取分页的数据 三个参数 queryset request self
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 对数据进行序列化
ser = Page1Serializer(instance=pager_roles, many=True)
return Response(ser.data)
# REST_FRAMEWORK
REST_FRAMEWORK = {
# 全局版本配置
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version',
# 全局解析器配置
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser'],
# 分页配置
'PAGE_SIZE': 3,
}
2.自定义分页器
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response # 使用这个Response可以显示rest framework页面 看着比较舒服
# 自定义分页器
class MyPageNumberPagination(PageNumberPagination):
# 自定义pagesize 默认去配置文件中读 可以自己设置
page_size = 2
# 可以在url中控制每页显示多少条数据的参数 size=3
# http://127.0.0.1:8000/api/v1/page1/?page=1&size=3 修改每页显示数据个数
page_size_query_param = 'size'
# 每页最多显示多少条
max_page_size = 5
# 控制分页的参数 page=1
page_query_param = 'page'
# 分页序列化器
class Page1Serializer(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = '__all__'
# 分页
class Page1View(APIView):
def get(self, request, *args, **kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = PageNumberPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 对数据进行序列化
ser = Page1Serializer(instance=pager_roles, many=True)
# return Response(ser.data)
# # 使用 get_paginated_response后会多显示 总数据个数以及上一页下一页的路由地址
return pg.get_paginated_response(ser.data)
使用 get_paginated_response后会多显示 总数据个数以及上一页下一页的路由地址
{
"count": 5,
"next": "http://127.0.0.1:8000/api/v1/page1/?page=2&size=2",
"previous": null,
"results": [
{
"id": 1,
"title": "学生"
},
{
"id": 2,
"title": "老师"
}
]
}
分页总结(有面试题)
-
数据量特别大,如何分页?
数据量特别大,每次请求mysql,都需要很长时间,如果一直不断的请求,mysql很可能会挂掉 最近再看rest frame源码,觉得搞的分页不错 是记录每页的第一条数据和最后一条数据来分页 比如每页显示10条数据,现在是第一页 那么就记录第1和第10的id,根据这两个id进行下一页和上一页的切换,不需要每次将每页数据全都读出来,大大解决了时间 并且如果有用户在url上自己输入特别大的页码,也会给mysql造成很大压力 rest framework中就有一种加密分页方法,每次分页返回的数据会携带上上一页和下一页的url,这段url是加密的字符串 因此用户不能够自己在url上随意控制分页,只能通过返回的url进行翻页
8.视图
视图基本使用
-
过去VIew
class PagerView(View): pass
-
现在APIView
# APIView 继承 View class Pager1View(APIView): pass
-
无用 GenericAPIView(和APIView差不多,几乎没有优化)
# 视图 from rest_framework.generics import GenericAPIView # GenericAPIView 继承 APIView class View1View(GenericAPIView): queryset = models.Role.objects.all() serializer_class = Page1Serializer pagination_class = MyPageNumberPagination def get(self, request, *args, **kwargs): # 获取数据 roles = self.get_queryset() # models.Role.objects.all() # 获取分页后的queryset对象 pager_roles = self.paginate_queryset(roles) # 序列化 ser = self.get_serializer(instance=pager_roles, many=True) return Response(ser.data)
-
GenericViewSet(ViewSetMixin, generics.GenericAPIView) 此时路由系统已经发生改变 ViewSetMixin重写了as_view方法
# as_view传入的参数 是Method方法对应的函数 get请求过来去执行list方法 re_path(r'^(?P<version>[v1|v2]+)/view1/$', views.View1View.as_view({'get': 'list'})),
# views.oy from rest_framework.viewsets import GenericViewSet class View1View(GenericViewSet): queryset = models.Role.objects.all() serializer_class = Page1Serializer pagination_class = MyPageNumberPagination def list(self, request, *args, **kwargs): # 获取数据 roles = self.get_queryset() # models.Role.objects.all() # 获取分页后的queryset对象 pager_roles = self.paginate_queryset(roles) # 序列化 ser = self.get_serializer(instance=pager_roles, many=True) return Response(ser.data)
-
最牛逼 ModelViewSet 非常好用
# 继承了很多类 分别完成了增删改查 ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet):
# urls.py # 路由 每个请求方法 对应一个函数 # 以后一个url写两个路由即可完成 所有的增删改查操作 urlpatterns = [ re_path(r'^(?P<version>[v1|v2]+)/view1/$', views.View1View.as_view({'get': 'list'})), re_path(r'^(?P<version>[v1|v2]+)/view1/(?P<pk>\d+)$', views.View1View.as_view({'get': 'retrieve', 'post': 'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})), ] # views.py 视图只需要写这样即可 from rest_framework.viewsets import ModelViewSet class View1View(ModelViewSet): queryset = models.Role.objects.all() serializer_class = Page1Serializer pagination_class = MyPageNumberPagination
视图总结
-
增删改查 ModelViewSet
-
增删 CreateModelMixin DestroyModelMixin GenericViewSet
-
复杂逻辑 GenericViewSet 或者 APIView
-
PS:还债
GenericViewSet.get_object() check_object_permissions has_object_permission
8.路由系统
urlpatterns = [
# http://127.0.0.1:8000/api/v1/view1/?format=json
re_path(r'^(?P<version>[v1|v2]+)/view1/$', views.View1View.as_view({'get': 'list'})),
# http://127.0.0.1:8000/api/v1/view1.json 可以直接路由访问.json格式
re_path(r'^(?P<version>[v1|v2]+)/view1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list'})),
# http://127.0.0.1:8000/api/v1/view1/1/?format=json 访问json格式
re_path(r'^(?P<version>[v1|v2]+)/view1/(?P<pk>\d+)$', views.View1View.as_view({'get': 'retrieve', 'post': 'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
# http://127.0.0.1:8000/api/v1/view1/1.json 可以直接路由访问.json格式
re_path(r'^(?P<version>[v1|v2]+)/view1/(?P<pk>\d+)\.(?P<format>\w+)$$', views.View1View.as_view({'get': 'retrieve', 'post': 'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]
自动生成路由
from django.urls import path, re_path, include
from api import views
from rest_framework import routers
# 创建路由对象
routers = routers.DefaultRouter()
# 注册路由对应视图
routers.register(r'rt', views.View1View)
urlpatterns = [
# 自动生成路由
re_path(r'^(?P<version>[v1|v2]+)/', include(routers.urls)),
]
# 自动生成路由会直接生成增删改查路由
api/ ^(?P<version>[v1|v2]+)/ ^rt/$ [name='role-list']
api/ ^(?P<version>[v1|v2]+)/ ^rt\.(?P<format>[a-z0-9]+)/?$ [name='role-list']
api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+)/$ [name='role-detail']
api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='role-detail']
api/ ^(?P<version>[v1|v2]+)/ ^$ [name='api-root']
api/ ^(?P<version>[v1|v2]+)/ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
9.渲染器
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
class TestView(APIView):
# 渲染器局部配置 其实就是渲染成什么格式的数据在页面上
# 通常都是JSON格式
# BrowsableAPIRenderer 则是让页面更好看一些而已
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
def get(self, request, *args, **kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = MyPageNumberPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 对数据进行序列化
ser = Page1Serializer(instance=pager_roles, many=True)
# 使用 get_paginated_response后会多显示 总数据个数以及上一页下一页的路由地址
return pg.get_paginated_response(ser.data)
# 全局配置
# REST_FRAMEWORK
REST_FRAMEWORK = {
# 全局版本配置
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version',
# 全局解析器配置
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser'],
# 分页配置
'PAGE_SIZE': 3,
# 渲染器配置
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'
]
}
# http://127.0.0.1:8000/api/v1/view1.json 可以直接路由访问.json格式
re_path(r'^(?P<version>[v1|v2]+)/view1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list'})),
# http://127.0.0.1:8000/api/v1/view1/1/?format=json 访问json格式
re_path(r'^(?P<version>[v1|v2]+)/view1/(?P<pk>\d+)$', views.View1View.as_view({'get': 'retrieve', 'post': 'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
# http://127.0.0.1:8000/api/v1/view1/1.json 可以直接路由访问.json格式
re_path(r'^(?P<version>[v1|v2]+)/view1/(?P<pk>\d+)\.(?P<format>\w+)$$', views.View1View.as_view({'get': 'retrieve', 'post': 'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]
自动生成路由
```python
from django.urls import path, re_path, include
from api import views
from rest_framework import routers
# 创建路由对象
routers = routers.DefaultRouter()
# 注册路由对应视图
routers.register(r'rt', views.View1View)
urlpatterns = [
# 自动生成路由
re_path(r'^(?P<version>[v1|v2]+)/', include(routers.urls)),
]
# 自动生成路由会直接生成增删改查路由
api/ ^(?P<version>[v1|v2]+)/ ^rt/$ [name='role-list']
api/ ^(?P<version>[v1|v2]+)/ ^rt\.(?P<format>[a-z0-9]+)/?$ [name='role-list']
api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+)/$ [name='role-detail']
api/ ^(?P<version>[v1|v2]+)/ ^rt/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='role-detail']
api/ ^(?P<version>[v1|v2]+)/ ^$ [name='api-root']
api/ ^(?P<version>[v1|v2]+)/ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']