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 表单提交

input ...

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, 从哪个又去找

posted @ 2019-09-10 13:41  learnacode  阅读(294)  评论(0编辑  收藏  举报