drf3
s7day130
今日内容概要
1.版本 *
2.解析器 *
3.序列化 ****
- 请求数据进行校验
- QuerySet进行序列化
4.分页 **
5.路由系统 **
6.视图 **
写视图的时候继承了哪些类
from rest_framework.viewsets import ModelViewSet
class AuthView(ModelViewSet):
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet): # 也继承了很多类
7.渲染器(页面渲染的时候更好看) *
剩下的执行流程 . 都是简单的功能
作业
谈谈面向对象
在那里用过封装,继承和多态, 聊用过, 错了也不要紧
封装 (crm的时候Django 的 stark组件 starkconfig)
标准答案:
(干了这么多年) : 现在快速想起来的时候,分页(django)
drf 中认证的时候 request
继承怎么体现 :
drf 视图继承的APIview , 继承了View
form表单 继承了form baseform 实例化, 继承了.isvalued
多态:
鸭子模型 : 体现在有一个函数, 有一个参数, 执行.xx方法, 有这个方法就够了 ,不需要知道这个对象 .
class Wx:
def send():
pass
class Email:
def send():
pass
def func(arg): # 只要能够send,就行
arg.send()
obj = Email()
func(obj)
2.Django生命周期
请求-
wsgi(django和服务器的约束(协议))-
wsgi , 是协议
wsgiref, 是实现了wsgi协议的一个模块. 模块本质: 一个socket服务端(django)
werkzeug, 是实现了wsgi协议的一个模块. 模块本质: 一个socket服务端(flask)
tornado, 是实现了wsgi协议的一个模块. 模块本质: 一个socket服务端 (区别)
uwsgi, 是实现了wsgi协议的一个模块. 模块本质: 一个socket服务端
3.django生命周期(restframework)
请求进来,路由系统,先走as_view(),然后在走dispatch,反射
def as_view():
def view():
return view
然后相当于执行的view()函数 as_view()
就成fbv那样了, 执行的view , 然后就是 view() : def view() : return self.dispatch() # drf , 请求封装 ,认证, 权限 , 节流, 反射对应的method执行
4.中间件&装饰器
中: 统一所有的操作 (做rbac(权限控制),用户登陆,csrf_token,session,黑名单,日志记录)
csrf_token:@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
session: 登陆成功, 给随机字符串,请求更进来,(给随机字符串)
请求进来, request , 进来给随机字符串
装: 每个单独操作
5.rest 框架原理
a.认证流程
就三个类 request类, perform , authre..类
方法:
两个, authcate : None ,(), 异常
authcate_head
b.权限
直接列表生成式执行方法
方法:
permission
c.节流
方法:
allow_request()
wait
内容详细 :
1.版本
a. 在url里传参 赋值的
-
# 组件取值 get class ParamVersion(object): def determine_version(self, request, *args, **kwargs): version = request.query_params.get('version') # http://127.0.0.1:8000/app01/users/?version=v1 return version class UsersView(APIView): versioning_class = ParamVersion def get(self, request, *args, **kwargs): # version = request._request.GET.get('version') # print(version) print(request.version) # 组件处理 return HttpResponse("用户列表")
用自带的
from rest_framework.versioning import BaseVersioning,QueryParameterVersioning REST_FRAMEWORK = { # settings 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version', # 只能固定形式传参 } class UsersView(APIView): # versioning_class = ParamVersion versioning_class = QueryParameterVersioning
INSTALLED_APPS = [
'rest_framework',] # 添加了之后 不报错了
HTTP 404 Not Found
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "Invalid version in query parameter."
}
b.在url里传参 urls.py(推荐使用)
-
url(r'^(?P
[v1|\v2]+)/users/$', views.UsersView.as_view()), -
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', #全局的 用处理版本的类 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version', # 只能固定形式传参 }
版本配置完
print(request.version) # 组件处理
获取就行 , 直接获取就行
1.版本源码
from rest_framework.versioning import QueryParameterVersioning
# def determine_version(): return version
class UsersView(APIView):
# versioning_class = ParamVersion
# versioning_class = QueryParameterVersioning
# versioning_class = URLPathVersioning # /v1/
def get(self, request, *args, **kwargs):
# version = request._request.GET.get('version')
# print(version)
self.dispatch
# initial--->
#version, scheme = self.determine_version(request, *args, **kwargs)
#request.version, request.versioning_scheme = version, scheme
# 获取处理版本的对象
print(request.versioning_scheme)
# 获取版本
print(request.version) # 组件处理
return HttpResponse("用户列表")
QueryParameterVersioning
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
反向生成url (上面的方法)
url(r'^(?P<version>[v1|\v2]+)/users/$', views.UsersView.as_view(), name='user'),
u1 = request.versioning_scheme.reverse(viewname='user', request=request)
print(u1) # http://127.0.0.1:8000/app01/v2/users/
内置的上面的
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version # 装进去了
# 自己用Django反向
u2 = reverse(viewname='user', kwargs={'version': 1}) # 指定版本了
print(u2) #/app01/1/users/
return HttpResponse("用户列表")
URLPathVersioning ---> GET /1.0/something/ HTTP/1.1
QueryParameterVersioning --->GET /something/?version=0.1 HTTP/1.1
HostNameVersioning ---> GET /something/ HTTP/1.1
Host: v1.example.com
总结:
使用 :
配置文件:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', #全局的 用处理版本的类
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version', # 只能固定形式传参
}
路由系统
urlpatterns = [
# url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls')),
]
urlpatterns = [
# url(r'^users/', views.UsersView.as_view()),
url(r'^(?P<version>[v1|\v2]+)/users/$', views.UsersView.as_view(), name='user'),
]
视图
class UsersView(APIView):
def get(self, request, *args, **kwargs):
# version = request._request.GET.get('version')
# print(version)
self.dispatch
# 1获取处理版本的对象
print(request.versioning_scheme)
# 2获取版本
print(request.version) # 组件处理 # request.version, request.versioning_scheme = version, scheme
# 用内置的反向(drf)
u1 = request.versioning_scheme.reverse(viewname='user', request=request)
print(u1)
# 自己用Django反向
u2 = reverse(viewname='user', kwargs={'version': 1}) # 指定版本了
print(u2) #/app01/1/users/
return HttpResponse("用户列表")
2.解析器
准备: Django:request.POST/request.body
1.如果请求头是 elif self.content_type == 'application/x-www-form-urlencoded':
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
POST中才有值,(去request.body中取数据)
2.数据格式要求:
name=alex%age=18&gender=男
如:
a.form 表单提交
b.ajax 提交
情况一
$.ajax({
url:..
type:POST,
data : {name:alex, age =18}}) # 内部转化name=alex%age=18&gender=男
$body 有值 : POST无
情况一
$.ajax({
url:..
type:POST,
header : {'Content-Type':'application/json'}
data : {name:alex, age =18}}) # 内部转化{name=alex%age=18&gender=男}
格式不对,头不对
$body 有值 : POST无
json.loads(request.body) 处理
rest_framework 解析器, 比Django强很多
from rest_framework.parsers import JSONParser, FormParser
class ParserView(APIView):
parser_classes = [JSONParser, FormParser, ]
# JSONParser只能解析 'application/json' # json常用
# FormParser只能解析'application/x-www-form-urlencoded'
def post(self, request, *args, **kwargs):
允许用户发送 JSON格式数据
a. content-type : application/json
b. {'name':'al', 'age':18}
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头和 parser_classes = [JSONParser, FormParser,]中支持的请求头进行比较
4. JSONParser 对象去请求体
5. request.data
"""
# 获取解析后的结果
print(request.data)# {'name': 1, 'gae': 18} 没去jsonload 自己处理的 # JSON
# <QueryDict: {'name': ['alex']}> # x-www-form-urlencoded' postman
return HttpResponse("parser ")
解析器源码
from rest_framework.request import Request
从request.data开始
data() : (in Request(2))
self._load_data_and_files()
def _load_data_and_files(self):
self._data, self._files = self._parse()
# 用户提交的请求头content_type的值
media_type = self.content_type # parser : parser_classes = [JSONParser, FormParser, ]
try:
parser = self.negotiator.select_parser(self, self.parsers) #parser父类中找
return (parsed, empty_files)
class FormParser(BaseParser):
media_type = 'application/x-www-form-urlencoded'
def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
return QueryDict(stream.read(), encoding=encoding)
class JSONParser(BaseParser):
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
decoded_stream = codecs.getreader(encoding)(stream)
parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc))
request : 认证 , 解析器, request 封装的
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_parsers(self):
return [parser() for parser in self.parser_classes]
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
全局的解析(form/ json)
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
其他的上传文件的时候,添加响应的解析器
from rest_framework.parsers import FileUploadParser
class TestView(APIView):
parser_classes = [FileUploadParser, ]
def post(self, request, filename, *args, **kwargs):
print(filename)
print(request.content_type)
print(request.FILES)
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
<input type="text" name="user" />
<input type="file" name="img">
<input type="submit" value="提交">
</form>
解析器:
全局配置:
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
使用 :
from rest_framework.parsers import JSONParser, FormParser
class ParserView(APIView):
# parser_classes = [JSONParser, FormParser, ]
# JSONParser只能解析 'application/json' # json常用
# FormParser只能解析'application/x-www-form-urlencoded'
def post(self, request, *args, **kwargs):
"""
允许用户发送 JSON格式数据
a. content-type : application/json
b. {'name':'al', 'age':18}
:param request:
:param args:
:param kwargs:
:return:
"""
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头和 parser_classes = [JSONParser, FormParser,]中支持的请求头进行比较
4. JSONParser 对象去请求体
5. request.data
"""
# 获取解析后的结果
print(request.data) # {'name': 1, 'gae': 18} 没去jsonload 自己处理的 # JSON
# <QueryDict: {'name': ['alex']}> # x-www-form-urlencoded' postman
self.dispatch
return HttpResponse("parser ")
源码流程 $ 本质 :
a.本质 : (扩展)
请求头
状态码
请求方法
b.源码流程
dispatch:request封装
request.data
3.序列化
url(r'^(?P<version>[v1|\v2]+)/role/$', views.RolesView.as_view(), name='role'),
class RolesView(APIView):
def get(self,request, *args, **kwargs):
roles = models.Role.objects.all().values('id', 'title')
roles = list(roles)
ret = json.dumps(roles) # values , list 改一下格式, 再dumps
return HttpResponse(ret)
[{"id": 1, "title": "\u5b89\u9759"}, {"id": 2, "title": "\u5584\u826f"}, {"id": 3, "title": "\u6d3b\u6cfc"}]
部分总结:
写类. 继承两种
1.serializers.Serializer
class UserInfoSerializer(serializers.Serializer):
# user_type = serializers.IntegerField() # [{"user_type": 1, "username": "liu", "password": "123"}, {"user_type": 2, "username": "yang", "password": "123"}]
# 但是 (1, '普通用户'), 后面的,而不是1
xxx = serializers.CharField(source='user_type') # 找到数据库对象 row.get_user_type_display 判断是否callable,自动调用()
xxxx = serializers.CharField(source='get_user_type_display') # 找到数据库对象 row.get_user_type_display 判断是否callable,自动调用()
MY = MyField(source='user_type')
# [{"xxx": "普通用户", "username": "liu", "password": "123"}, lambda(用)
username = serializers.CharField() # 11对应,但是指定字段了可以随便写
password = serializers.CharField()
gp = serializers.CharField(source='group.id') # 对象 , id是 源码:. sorcelit: .取可以一直...如果foreign有
gp = serializers.CharField(source='group.title') # 对象 , id是 源码:. sorcelit: .取可以一直...如果foreign有
rls = serializers.CharField(source='roles.all') # 不用加(),自动给加
rls = serializers.SerializerMethodField() # 自定义显示
# 有choice的时候和foreignkey的时候都可用source, 但是做不到细粒度
# 细粒度用函数
def get_rls(self, row): # get开头 字段名结尾 传对象,当前行的对象
# 写死的
role_obj_list = row.roles.all() # row是什么? 老师没说啊 不论写什么,都是user默认对应的role对象,源码处理
# role_obj_list = models.Role.objects.all() # 如果这样的话,就是所有的对应的role
ret = []
for item in role_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret
# "rls": [{"id": 1, "title": "安静"}, {"id": 2, "title": "善良"}, {"id": 3, "title": "活泼"}]}
2.serializers.ModelSerializer(字段多,省事)
# 另一种方法:换个model
class UserInfoSerializer(serializers.ModelSerializer):
oooo = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField() # 自定义显示
group = serializers.CharField(source='group.title') # 对象 , id是 源码:. sorcelit: .取可以一直...如果foreign有
class Meta:
model = models.UserInfo # 自动找关系, 生成字段 # models.AutoField: IntegerField,
fields = "__all__"
# 简陋 :完成基本的操作 , [{"id": 1, "user_type": 1, "username": "liu", "password": "123", "group": 1, "roles": [1, 2, 3]},
# 自定制 : 因为ModelSerializer 是继承的 Serializer , 所以可以都
fields = ['id', 'username', 'password', 'oooo', 'rls', 'group'] # 也当做字段 # [{"id": 1, "username": "liu", "password": "123", "oooo": "普通用户"},
# extra_kwargs = {'group': {'source': 'group.title'}, } # 作用是一样的 # gp = serializers.CharField(source='group.title') # 对象 , id是 源码:. sorcelit: .取可以一直...如果foreign有
def get_rls(self, row):
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret
字段
a.username = serializers.CharField() # 11对应,但是指定字段了可以随便写
b.rls = serializers.SerializerMethodField() # 自定义显示
def get_rls(self, row):
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret
c. 自定类
CharField之流
# 定制功能
class MyField(serializers.CharField):
def to_representation(self, value):
return '这写什么就返回什么'
pass # 什么不写就是和CharField一样
MY = MyField(source='user_type')
3.自动序列化连表
# alex 版
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo # 自动找关系, 生成字段 # models.AutoField: IntegerField,
fields = "__all__" # 简陋版
fields = ['id', 'username', 'password', 'group', 'roles'] # 也当做字段 # [{"id": 1, "username": "liu", "password": "123", "oooo": "普通用户"},
depth = 1 # 丰富版 # userinfo 往里拿一层 层数越多,越慢
# 0 - 10 之间 34层很多了
4.生成连接
4.1
url(r'^(?P<version>[v1|\v2]+)/group/(?P<pk>\d+)$', views.GroupView.as_view(), name='gp'),
class UserInfoSerializer(serializers.ModelSerializer):
# 要么带url 要么写上在url
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk') # id 反向生成url , 给起个名
# source有点问题 #pk 默认
class Meta:
model = models.UserInfo # 自动找关系, 生成字段 # models.AutoField: IntegerField,
fields = "__all__" # 简陋版
fields = ['id', 'username', 'password', 'group', 'roles'] # 也当做字段 # [{"id": 1, "username": "liu", "password": "123", "oooo": "普通用户"},
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
ser = UserInfoSerializer(instance=users, many=True, context={'request': request}) #根据id 反向生成 url
print(ser.data)# [{"id": 1, "username": "liu", "password": "123", "group": "http://127.0.0.1:8000/app01/v2/group/1", "roles": [1, 2, 3]},
4.2
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserGroup # 自动找关系, 生成字段 # models.AutoField: IntegerField,
fields = "__all__" # 简陋版
class GroupView(APIView): # 查看id
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.UserGroup.objects.filter(pk=pk).first()
ser = GroupSerializer(instance=obj, many=False)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
请求
4.1
http://127.0.0.1:8000/app01/v2/userinfo/
[{"id": 1, "username": "liu", "password": "123", "group": "http://127.0.0.1:8000/app01/v2/group/1", "roles": [1, 2, 3]},
{"id": 2, "username": "yang", "password": "123", "group": "http://127.0.0.1:8000/app01/v2/group/1", "roles": [1]}]
4.2
http://127.0.0.1:8000/app01/v2/group/1
{"id": 1, "title": "A组"}
序列化源码
实例化
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]},
def get_attribute(self, instance):
try:
# instance 对象
# source_attrs : group.title / get_user_type_display/ roles.all
return get_attribute(instance, self.source_attrs)
def get_attribute(instance, attrs):
# source_attrs : [group,title] / get_user_type_display/ roles.all
for attr in attrs:
if is_simple_callable(instance):
try:
instance = instance()
def is_simple_callable(obj):
if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
def isfunction(object):
return isinstance(object, types.FunctionType) #判断是否是函数类型
ser.data 入口, 去父类找
功能二. 请求数据校验
####################### 验证 ##########################
class PasswordValidator(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if not value.startswith(self.base):
message = '标题必须以%s为开头' % self.base
# {'title': [ErrorDetail(string='标题必须以人生为开头', code='invalid')]}
raise serializers.ValidationError(message)
def set_context(self, serializer_field):
"""
This hook is called by the serializer instance,
prior to the validation call being made.
"""
# 执行验证之前调用,serializer_fields是当前字段对象
pass
class UserGroupSerializer(serializers.Serializer):
# title = serializers.CharField()
title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[PasswordValidator('人生')]) # {'title': [ErrorDetail(string='标题不能为空', code='required')]}
# 钩子函数自定义验证规则 # 以什么开头
class UserGroupView(APIView):
def post(self, request, *args, **kwargs):
print(request.data)
ser = UserGroupSerializer(data=request.data)
# 做校验
if ser.is_valid():
print(ser.validated_data) # # OrderedDict([('title', '1')])
print(ser.validated_data['title']) # # OrderedDict([('title', '1')])
else:
print(ser.errors) # {'title': [ErrorDetail(string='This field is required.', code='required')]}
return HttpResponse('提交数据 ')
# 有构造函数, 去源码找 , 不那么写,
问: 自定义验证规则时,需要钩子函数?请问钩子函数如何写?
一个个取,还可以生成url , 还有..
验证
is_valid()去看, sourse, 从哪个又去找