zoukankan      html  css  js  c++  java
  • 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": "u5b89u9759"}, {"id": 2, "title": "u5584u826f"}, {"id": 3, "title": "u6d3bu6cfc"}]
    

    部分总结:

    ​ 写类. 继承两种

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

  • 相关阅读:
    css基础教程
    网页加载-上下幕布分开
    Vue-Aixos
    webpack学习
    Vue、Element 路由跳转,左侧菜单高亮显示,页面刷新不会改变当前高亮菜单
    Vue知识概括
    JSON.parse() 与 JSON.stringify()
    Bootstrap4 样式笔记
    ES6基础语法
    V-model 双向绑定的原理是什么?
  • 原文地址:https://www.cnblogs.com/Doner/p/11497049.html
Copyright © 2011-2022 走看看