drf4
s7day131
今日内容概要:
-
分页
越往后翻,越慢的问题
(分页页数少, 展示少, 上下页, 减少页数)
-
视图
-
路由
-
渲染
-
django组件: content-type
内容回顾:
1.rest规范
-
请求是一类:
https 域名 版本 资源(名词) 过滤
请求头: method
响应:
状态码 出错 结果集 Hypermedia API
2.rest framework
-
django请求生命周期
view dispatch 封装request initial(认证,权限,节流)
版本: url 请求头 spacename (一般url)
反射到视图函数: 拿数据:序列化,解析器不同
Django中: (drf中有解析器不会这样)
request.body中有值, post没有值? 传请求体了, contype=url_encode
数据格式: a=b&c=d...这两个条件
字段(Charfield()), method(get_) , url(hey....)
many=True , ser.data
-
要求: 前4个组件(源码)
-
序列化
6期师兄:
韧劲. 兵哥哥 . 学了个安卓, 中软(和华为的工作), 被裁掉了
去上海找,(问源码会不会?不会!) (我允许自己学不会) 6.30上自习, 就两天不在教室, 一直到最后, 过年回家没有干别的, 也在看视频(学习)
解析器
返回属性和返回方法的
"""
many=True , 执行ListSerializer对象 的构造方法
many = flase , 执行UserInfoSerializer对象 的构造方法
"""
ser = UserInfoSerializer(instance=users, many=True, context={'request': request}) #根据id 反向生成 url # [{"id": 1, "username": "liu", "password": "123", "group": "http://127.0.0.1:8000/app01/v2/group/1", "roles": [1, 2, 3]},
print(ser)
Serializer
BaseSerializer
def __new__(cls, *args, **kwargs):
# We override this method in order to automagically create
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False): # 没有many就是false
# many = True , 对queryset进行 处理
return cls.many_init(*args, **kwargs)
# many = False 对对象进行处理 返回一个对象实例化的 构造方法 instance
return super().__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
## 执行这里的构造方法
class ListSerializer(BaseSerializer):
def __init__(self, *args, **kwargs):
print('构造方法')
def data(self):
ret = super().data
return ReturnList(ret, serializer=self)
def data():
if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance)
ListSerializer源码下的
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.all() if isinstance(data, models.Manager) else data
return [
self.child.to_representation(item) for item in iterable
]
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
# CharField.get_attribute(对象) # 对象.username
attribute = field.get_attribute(instance)
except SkipField:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
"""
{
id:1, CharField
pwd:123, CharField
group:obj HyperlinkedIdentityField
}
"""
ret[field.field_name] = field.to_representation(attribute)
class HyperlinkedIdentityField(HyperlinkedRelatedField):
没有去父类中找
class HyperlinkedRelatedField(RelatedField):
def to_representation(self, value):
try: # value 对象, 对象反射取值
url = self.get_url(value, self.view_name, request, format)
def get_url(self, obj, view_name, request, format):
lookup_value = getattr(obj, self.lookup_field)
lookup_field
self.lookup_field = kwargs.pop('lookup_field', self.lookup_field)
所以
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk') # id 反向生成url , 给起个名
get_url中:
lookup_value = getattr(obj, self.lookup_field)
kwargs = {self.lookup_url_kwarg: lookup_value}
# {xxx:1}
# reverse(view_name='gp')
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
# 还有返回url的步骤
self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field)
{
id:1, CharField
pwd:123, CharField
group:obj ->group:http://group/1
}
小bug的source 反向
value = self.to_internal_value(data)
验证源码
# 钩子函数自定义验证规则 # 以什么开头
def validate_title(self, value):
print(value)
from rest_framework import exceptions
raise exceptions.ValidationError('看你不顺眼')
return value
for field in fields:
# validate_字段名
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = field.get_value(data)
try:
# 执行字段本身的正则
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
# 验证的钩子方法
validated_value = validate_method(validated_value)
1.分页
a.分页, 看第n页, 每页显示n条数据;
b.分页, 在第n个位置, 向后查看n条数据
c.加密分页(上一页和下一页) : 速度也不慢(本质上:第一页的最大id和最小id记住,大于,不扫描)
a.分页, 看第n页, 每页显示n条数据;
class PageNumberPagination(BasePagination):
page_query_param = 'page'
所以访问http://127.0.0.1:8000/app01/v2/page1/?page=2
自定制分页 继承它
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
page_size = 2
page_size_query_param = 'size' # 合上面的是一体的
max_page_size = None
page_query_param = 'page' # 页码
http://127.0.0.1:8000/app01/v2/page1/?page=2&size=3
↓
[
{
"id": 4,
"title": "张三"
},
{
"id": 5,
"title": "李四"
},
{
"id": 6,
"title": "王五"
}
class Pager1View(APIView):
def get(self, request, *args, **kwargs):
# 获取所有数据
roles = models.Role.objects.all()
ser = PagerSerialiser(instance=roles, many=True)
ret = json.dumps(ser.data, ensure_ascii=False)
# return HttpResponse(ret)
# 创建分页对象
pg = MyPageNumberPagination()
# 在数据中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 分页
print(pager_roles) # [<Role: Role object>, <Role: Role object>] 2个对象
# 对数据进行序列化
ser = PagerSerialiser(instance=pager_roles, many=True)
# return Response(ser.data) # 返回数据
return pg.get_paginated_response(ser.data) # 上一页下一页也给做了
b.分页, 在第n个位置, 向后查看n条数据
class MyPageNumberPagination(LimitOffsetPagination):
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
换个继承的类,修改数据
# 创建分页对象
pg = MyPageNumberPagination()
# pg = LimitOffsetPagination()
c.加密分页
(上一页和下一页) : 速度也不慢(本质上:第一页的最大id和最小id记住,大于,不扫描)
class MyPageNumberPagination(CursorPagination):
cursor_query_param = 'cursor'
page_size = 2
ordering = 'id' # 按id排序
page_size_query_param = None
max_page_size = None
http://127.0.0.1:8000/app01/v2/page1/
"next": "http://127.0.0.1:8000/app01/v2/page1/?cursor=cD00",
部分源码
# current_position : id 前后处理, 比较方便,比较两个
if self.cursor.reverse != is_reversed:
kwargs = {order_attr + '__lt': current_position}
else:
kwargs = {order_attr + '__gt': current_position}
先讲渲染
首先settings
INSTALLED_APPS = [ 'rest_framework',]
# 渲染器
from rest_framework.response import Response
分页总结:
放到mysql中:
设计表结构
连表查询(基础)
-
数据量大的话,如何做分页?
最大值,最小值(有重合的点都要说出来,边缘的点)
问一个答一个,失败率非常高,每一个简历对应的都有答案(必须会)
-
临时想到的题? 最近在看的题? 你怎么办?django如果没有中间件怎么办?
-
主动? 你自己说 80你会, 而不是他一直在问,一直不会
数据库性能相关? 索引
没问你, 你要说, 再看drf,数据大的时候,怎么样 , drf分页挺好的,
根据最大值,最小值,.处理, 但是数据大的时候,一样慢, 他又把页数做了个加密.挺好
上一页,下一页,这样
带到你会的领域
2.视图
a.过去
class PagePager1View(View):
pass
b. 现在
class Pager1View(APIView): # view
pass
c. 无用
又继承了APIView(三层继承) , 里面封装了方法, 但是还是用的
# 提供了许多方法
from rest_framework.generics import GenericAPIView
class View1View(GenericAPIView):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def get(self, request, *args, **kwargs):
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
# [1,1000, ] [1,10]
pager_roles = self.paginate_queryset(roles)
# 序列化 实例化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
d.GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): 没背景...家底殷实
路由:
url(r'^(?P
视图:
from rest_framework.viewsets import GenericViewSet
class View1View(GenericViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def list(self, request, *args, **kwargs): # 重写了as_view() 那个父类
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
# [1,1000, ] [1,10]
pager_roles = self.paginate_queryset(roles)
# 序列化 实例化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
e.路由系统:两个视图
url(r'^(?P<version>[v1|\v2]+)/view1/$', views.View1View.as_view({'get': 'list', 'post': 'create'})),
url(r'^(?P<version>[v1|\v2]+)/view1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve', 'del
views:(第一种写法)
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.mixins import ListModelMixin
class View1View(ListModelMixin, GenericViewSet, CreateModelMixin):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
路由:
url(r'^(?P
请求:
http://127.0.0.1:8000/app01/v1/view1/
显示:
views:(第二种写法)
class View1View(ModelViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
ModelViewSet
继承的类
class ModelViewSet(mixins.CreateModelMixin, # post 更新
mixins.RetrieveModelMixin, # get 执行单条数据
mixins.UpdateModelMixin, # update patch 局部更新和全局
mixins.DestroyModelMixin, # delete 删除 destroy
mixins.ListModelMixin, # 分页吗?
GenericViewSet): # 继承了两个类 就是ViewSetMixin, generics.GenericAPIView
路由:
url(r'^(?P<version>[v1|\v2]+)/view1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
# get执行单条数据了 有id update partical_update 全部更新和局部更新
请求: (因为是单条,传id)
http://127.0.0.1:8000/app01/v1/view1/1/
显示:
继承越多,写的越少, model继承了6个类, 增删改查更新Gener(也继承了两个)
总结:
简单的 用ModelViewSet
非常复杂的用 APIVIew 和GenericViewSet
不过用 GEn的 得在函数里区分,而不用的话,在model里的url中自己就有区分走不同的路由
url(r'^(?P<version>[v1|\v2]+)/view1/$', views.View1View.as_view({'get': 'list', 'post': 'create'})),
url(r'^(?P<version>[v1|\v2]+)/view1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
类似于fbv和cbv if 判断 和 走 dispatch?
没有单条的APIView更简洁
a.增删改查 ModelViewSet
b.增删 CreateModelMixin , DestroyModelMixin, GenericViewSet
c.复杂逻辑
GenericViewSet 和 APIVIew
权限: (单个调用的流程)
GenericViewSet.get_object
check_object_permissions
has_object_permission
3.路由
a.
url(r'^(?P
b.
url(r'^(?P
c.
# <http://127.0.0.1:8000/app01/v1/view1/2/?format=json>
url(r'^(?P<version>[v1|\v2]+)/view1/$', views.View1View.as_view({'get': 'list', 'post': 'create'})),
# http://127.0.0.1:8000/app01/v1/view1.json
url(r'^(?P<version>[v1|\v2]+)/view1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list', 'post': 'create'})),
url(r'^(?P<version>[v1|\v2]+)/view1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
# http://127.0.0.1:8000/app01/v1/view1/2.json
url(r'^(?P<version>[v1|\v2]+)/view1/(?P<pk>\d+)\.(?P<format>\w+)$', views.View1View.as_view({'g
混合这用
路由:
url(r'^(?P<version>[v1|\v2]+)/view1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list', 'post': 'create'})), # \转义
请求:
http://127.0.0.1:8000/app01/v1/view1.json
响应:
{"count":8,"next":"http://127.0.0.1:8000/app01/v1/view1.json?page=2","previous":null,"results":[{"id":2,"title":"善良"},{"id":3,"title":"活泼"}]}
urls配置
from django.conf.urls import url, include
# 自动生成路由 (4个,前缀是xxxx,或者rt)
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'xxxx', views.View1View)
router.register(r'rt', views.View1View)
#url(r'^', include(router.urls)),
# 把版本带上
url(r'^(?P<version>[v1|\v2]+)/', include(router.urls)),
自动生成路由
-
^app01/ ^(?P<version>[v1|\v2]+)/ ^xxxx/$ [name='role-list'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^xxxx\.(?P<format>[a-z0-9]+)/?$ [name='role-list'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^xxxx/(?P<pk>[^/.]+)/$ [name='role-detail'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^xxxx/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='role-detail'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^rt/$ [name='role-list'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^rt\.(?P<format>[a-z0-9]+)/?$ [name='role-list'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^rt/(?P<pk>[^/.]+)/$ [name='role-detail'] ^app01/ ^(?P<version>[v1|\v2]+)/ ^rt/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='role-detail']
增删改查(时可以用)
4.渲染
请求:
http://127.0.0.1:8000/app01/v1/view1/2/?format=json
响应:
{"id":2,"title":"善良"}
这是渲染,以json形式返回
http://127.0.0.1:8000/app01/v1/view1/2/
这是路由, 返回页面
http://127.0.0.1:8000/app01/v1/test/?format=json
renderer_classes = [JSONRenderer,] # 渲染
renderer_classes = [BrowsableAPIRenderer,] # 渲染
renderer_classes = [AdminRenderer] # 渲染
http://127.0.0.1:8000/app01/v1/test/?format=admin
局部处理
# from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer, AdminRenderer, HTMLFormRenderer
# HTMLFormRenderer : 显示表单,form
class TestsView(APIView):
# renderer_classes = [JSONRenderer, BrowsableAPIRenderer, AdminRenderer] # 渲染
# renderer_classes = [JSONRenderer] # 够了
# renderer_classes = [JSONRenderer, BrowsableAPIRenderer] # 好看
全局处理
配置文件:
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'
]
今日作业 :
-
序列化
-
视图继承流程
-
写代码
a. vue
登录页面
输入用户名和密码, 发送ajax请求
用户列表页面,将用户数据显示在页面中
b. django rest framework
-/auth/
返回token
-/student/
返回学生列表
-/student/\d+/
返回学生详细信息
c.节流:
控制
-匿名用户 2分钟10次
-正常用户 2分钟10次
ps:
CORS