zoukankan      html  css  js  c++  java
  • DRF源码分析

    题外:

    django中间件

    • process_request

    • process_view

    • process_response

    • process_exception

    • process_render_template

    执行顺序:

    1. process_request ——> 路由匹配——> 执行process_view ——> 执行视图函数 ——>执行process_response;

    2. 如果执行视图函数出错——>执行process_exception;

    3. 如果视图函数中有render模板渲染——>执行process_render_template

    中间件的使用场景:

    • 用户认证(登录校验)

    • 权限验证

    • csrf实现

      • django中的csrf如何实现?

        原理就是在用户请求进来时验证是否携带随机token;

        看起来通过process_request或process_view都能完成,但它是通过process_view方法实现:

        • 它需要先检查视图是否被@csrf_exempt装饰 (免除csrf验证),

          如果在process_request中拦截,就无法做到这一点。

        • 去请求体或cookie中获取token


    django中的FBV,CBV理解

    django的请求生命周期

    请求 ——> wsgi(wsgiref模块) ——> django中间件 ——> 视图(FBV: 执行视图函数,CBV: 通过dispatch反射类方法)

    • wsgi:

      • wsgi,一个webserver通信协议

      • wsgiref,是实现了wsgi协议的一个模块(django使用),本质:一个socket服务端

      • werkzeug,是实现了wsgi协议的一个模块(flask使用),本质:一个socket服务端

      • tornado是自己实现了socket服务,当然也可以使用wsgiref和werkzeug

      • uwsgi,也是实现了wsgi协议的一个模块,部署时使用。

    1.FBV

    FBV是指基于函数的视图

    2.CBV

    CBV是基于类的视图

    CBV:基于反射实现根据不同请求方式,执行类视图中不同的方法

    原理:请求进来会先调用dispatch方法,方法中根据reuqest.method反射执行类视图中的各类方法(GET/POST/PATCH/PUT/DELETE),然后返回响应对象;整个请求处理过程在dispatch中完成

    原码流程:url ——>类视图的as_view中的view方法 ——> dispatch方法 ;代码如下:

    (原生django中的请求处理流程)django.views.generic.base.py:

    @classonlymethod
    def as_view(cls, **initkwargs):
        ......
        
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
    
        # take name and docstring from class
        update_wrapper(view, cls, updated=())
    
        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
    
        def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            if request.method.lower() in self.http_method_names:
                # handler 就是我们定义的的视图方法
                handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)  # 返回响应对象
    

    我们可以在请求处理前或响应前做一些操作,类视图中,重写dispatch方法:

    def dispatch(self, request, *args,**kwargs):
        print(before)
        res = super().dispatch(request, *args,**kwargs)
        print('after')
        return res
    

    drf中的请求流程

    drf的APIView重写了 dispatch 方法,在 initialize_request 方法中对django的请求request做了二次封装,封装的内容包括:

    • django的request
    • 所有认证类实例化后的对象列表authenticators
    • 所有解析器类实例化后的对象列表parsers

    将这些都保存在当前新的request对象上,通过request._request 可以获取原生的request对象;

    request = self.initialize_request(request, *args, **kwargs)

        def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    		# 对request进行二次封装
            return Request(
                request,  # django 的 request
                parsers=self.get_parsers(),  # 配置的所有解析器
                authenticators=self.get_authenticators(), # 配置的所有认证类的实例
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    

    在我们的视图方法调用前,处理一些额外事情:认证,权限检测,限流

    self.initial(request, *args, **kwargs)

        def initial(self, request, *args, **kwargs):
            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)  # 认证 ,调用request.py中的user
            self.check_permissions(request)  # 权限校验
            self.check_throttles(request)  # 限流
    

    认证

    1.源码执行流程

    # views.py: APIView
    def dispatch():
        # 新的request中封装原生request和认证类的对象列表
        self.initialize_request(self, request, *args, **kwargs)
    	# 执行视图方法前的操作
        self.initial(request)
    
    def initialize_request(self, request, *args, **kwargs):
        parser_context = self.get_parser_context(request)
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
        
    def initial():
        self.perform_authentication(request)
    
    def perform_authentication():
        request.user
    
    # request.py: Request
    @property
    def user():
        self._authenticate()
        
    def _authenticate()
        # 使用认证实例对象列表依次验证
        for authenticator in self.authenticators:
        # 调用认证类中的authenticate方法进行身份验证
        user_auth_tuple = authenticator.authenticate(self)
    
    

    2.关键函数分析

    self._authenticate(self) 依次尝试使用每个认证实例对请求进行身份验证。

        def _authenticate(self):
            # 使用认证实例对象列表依次验证
            for authenticator in self.authenticators:
                try:
                    # 调用authenticate方法验证
                    # 1.如果authenticate方法抛出异常,执行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
    		# 如果所有认证返回的都是None,则执行以下函数,设置一个匿名用户
            self._not_authenticated()
    

    3.自定义认证类

    必须继承 BaseAuthentication,并实现 authenticate 方法

    class MyAuthentication(BaseAuthentication):
        def authenticate(self, request):
    	# token = request.query_params.get('token')  # 按get请求获取,
            # if not token:                              # 获取不到时
            #     token = request.data.get('token')      # 按POST请求获取
            token = request.META.get('HTTP_AUTHORIZATION', None)
    
            # 登陆判断 验证jwt
            try:
                payload = jwt_encode_handler(token)
            except:
                raise exceptions.AuthenticationFailed()   #返回一个403状态码
            user =  User.objects.get(phone=payload['phone'])
            return user, token
    

    权限

    权限分为两种:视图访问权限 和 对象访问权限

    1.源码执行流程

    1.1 视图访问权限:
    # views.py: APIView
    def dispatch():
        self.initialize_request(self, request, *args, **kwargs)
    	# 执行视图方法前的操作
        self.initial(request)
    
    def initial():
        self.check_permissions(request)
        
    def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
            
    def get_permissions(self):
        return [permission() for permission in self.permission_classes]
    
    1.2 对象访问权限

    在访问某个对象具体详情时,调用 get_object() 方法使用

    # generics.py: GenericAPIView
    def get_object(self):
        self.check_object_permissions(self.request, obj)
    
    # views.py: APIView
    def check_object_permissions(self, request, obj):
        for permission in self.get_permissions():
            if not permission.has_object_permission(request, self, obj):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
                
    def get_permissions(self):
        return [permission() for permission in self.permission_classes]
    

    2.自定义权限类

    必须继承 BasePermission,并实现 has_permission 方法

    class MyPermission(BasePermission):
        message = "您没有权限访问"
        def has_permission(self, request, view):
            """视图访问权限控制"""
    	return True  # True有权访问/False无权访问
        
        def has_object_permission(self, request, view, obj):
            """对象访问权限控制,调用 get_object() 时使用"""
            return True
    

    节流

    1.源码执行流程

    # views.py: APIView
    def dispatch():
        self.initialize_request(self, request, *args, **kwargs)
    	# 执行视图方法前的操作
        self.initial(request)
    
    def initial():
        self.check_throttles(request)
        
    def check_throttles(self, request):
        for throttle in self.get_throttles():
            if not throttle.allow_request(request, self):
                self.throttled(request, throttle.wait())
            
    def get_throttles(self):
        return [throttle() for throttle in self.throttle_classes]
    

    2.自定义节流类

    2.1 继承 基础的节流类 BaseThrottle:

    需要实现 allow_requestwait 方法

    # 访问记录 实际应该放在缓存(drf默认节流类就是存放在缓存);放在全局变量,程序重启会清空;
    VISIT_RECORD = {} 
    
    class VisitThrottle(BaseThrottle):
        """60s内只能访问3次"""
        def __init__(self):
            self.history = None
        
        def allow_request(self, request, view):
            # 获取用户ip (也可以基于用户名来限制)
            # remote_addr = request.META.get('REMOTE_ADDR')
            remote_addr = self.get_ident(request) # 调用父类方法获取唯一标识
            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()  # 60s以前的记录直接删掉
            
            if len(history) < 3:
                history.insert(0, ctime)
                return True
            
            # 返回True允许访问, False/None表示超过频率,被限制
    	
        def wait(self):
            """提示还需要等待n秒才能访问"""
            ctime = time.time()
            n = 60 - (ctime - self.history[-1])
    	return n
    
    2.2 继承 内置节流类 SimpleRateThrottle:

    基本功能都已经帮我们实现好了,只需实现get_cache_key ,再配置settings就行:

    class VisitThrottle(SimpleRateThrottle):
        """匿名用户节流类(根据ip)"""
        scope = ‘yds’  # 设置一个key,用于去settings配置取值(需自己全局配置)
        
        def get_cache_key(self, request, view):
            """获取缓存的key:必须自己实现这个方法"""
            return self.get_ident(request)  # 对匿名用户做限制
        	
    class UserVisitThrottle(SimpleRateThrottle):
        """登录用户节流类(根据用户名)"""
        scope = ‘yds_user’  # 设置一个key,用于去settings配置取值(需自己全局配置)
        
        def get_cache_key(self, request, view):
            return request.user  # 对登录用户做限制
    

    还需全局配置:

    REST_FRAMEWORK = {
        # 如果有两个节流类不要都配置在全局,可以指定接口局部使用
        # "DEFAULT_THROTTLE_CLASSES": ["utils.throttle.VisitThrottle",]
        "DEFAULT_THROTTLE_RATES": {
    		"yds": '3/m'  # 匿名用户每分钟3次
            "yds_user": '10/m' # 登录用户每分钟10次
    	}
    }
    

    SimpleRateThrottle 源码的方法:

        def get_cache_key(self, request, view):
    
        def get_rate(self):
    
        def parse_rate(self, rate):
    
        def allow_request(self, request, view):
    
        def throttle_success(self):
    
        def throttle_failure(self):
    
        def wait(self):
    

    解析器

    1.解析器是干什么的

    解析器:就是根据请求头Content-Type对请求体的数据进行解析,变成我们代码中的数据结构

    请求头 Content-Type : 指明发给后端的数据的类型(也就是请求体格式)

    根据不同的 Content-Type 需使用合适的解析器进行解析

    2.django的解析器

    request.POSTrequest.body 一般情况下都可以用于获取前端传来的数据,request.POST是由request.body在特定条件下解析而来;所以有时request.POST不一定能取到传来数据,它需要满足两个要求:

    • 请求头要求:

      Content-Type: 'application/x-www-form-urlencode'

    • 数据格式要求:

      name=alex&age=18&gender=男

    django只支持满足以上要求,才会去request.body中解析数据赋给request.POST

    如: 表单提交 和 ajax提交

    ​ 它们默认headers:{"Content-Type": "application/x-www-form-urlencode"},并且在内部都会将data中的数据转化为 name=alex&age=18&gender=男 这种形式

    ​ 但ajax也可以自己定制请求头参数,其它情形则无法解析:

    情况一:
    $ajax({
    	url:...,
    	type:POST,
    	headers:{"Content-Type":"application/json"}
    	data:{name:alex, age=18}  # 内部转化`name=alex&age=18`
    })
    # body有值,POST无
    情况二:
    $ajax({
    	url:...,
    	type:POST,
    	headers:{"Content-Type":"application/json"}
    	data:JSON.stringfy({name:alex,age=18})
    })
    # body有值,POST无
    #取值: json.loads(request.body.decode())
    

    3. drf的解析器

    代码位于 rest_framework.parsers.py

    3.1 解析器介绍

    drf的内置解析器常用的有 JsonParser 和 FormParser两个类 ,还有两个文件上传的解析器类 MultiPartParser 和 FileUploadParser

    JsonParser:支持解析 请求头{"Content-Type":"application/json"} 的json数据

    FormParser:支持解析 请求头{"Content-Type":"application/x-www-form-urlencode"}

    视图中设置解析器

    class ParserView(APIView):
        parser_classes = [JsonParser, FormParser] # 同时支持两种头的解析
    	
        def post(self,request)
    	调用request.data时会去执行解析操作
    	# 1.获取用户请求
    	# 2.获取用户请求体
    	# 3.根据请求头 和 解析器parser_classer=[JsonParser, FormParser]支持的头进行比较
    	# 5.使用合适的解析器解析请求体,赋值给request.data
    		
    

    3.2 源码执行流程

    drf中使用解析器,是由 request.data 去触发调用解析器类

    # 请求进来时dispatch()中的initialize_request()方法封装所有解析器类实例到request对象
    
    # 调用request.data
    # request.py: Request
    @property
    def data(self):
        self._load_data_and_files()
    
    def _load_data_and_files(self):
        self._parse()
    
    def _parse(self): # self 就是请求对象request
        # 获取用户提交的请求头Content-Type
    	media_type = self.content_type 
        # 请求头匹配解析器,选择合适的解析器类;方法下面详解
        parser = self.negotiator.select_parser(self, self.parsers)
        # 调用选好的解析器的parser方法进行解析
        parser.parse(stream, media_type, self.parser_context)
        
    def select_parser(self, request, parsers):
        # content_type:用户提交的头
        # media_type:解析器设置的头
        # 使用 content_type 和我们配置的解析器类中的 media_type 进行匹配
        for parser in parsers:
            if media_type_matches(parser.media_type, request.content_type):
                return parser
        return None
    

    序列化

    将模型对象转化为字典格式,再转化为json来进行数据传输

    1. django中的序列化

    def get(request):
        roles = models.Role.objects.all().values('id','title')
        roles = list(roles)
        ret = json.dumps(roles,ensure_ascii=False)
        return HttpResponse(ret)
    

    2. drf序列化

    2.1 使用

    2.1.1 自定义字段的显示

    Serializer:

    user_type_choice = (
        (1,'普通用户'),
        (2,'vip'),
        (3,'svip'),
    )
    class User(models.Model):
        username = ...
        pasword = ...
        user_type =  IntegerField(choices=user_type_choice) 
        group = models.ForeignKey('UserGroup')  # 所在组
        roles = models.ManyToManyField('Role')  # 喜欢的角色
    
    class UserSerializer(serializers.Serializer):
        xxx = serializer.CharField(source='user_type') # row.user_type
        # get_user_type_display() 用来取choice字段的显示值
        ooo = serializer.CharField(source='get_user_type_display') # row.get_user_type_display()
        gp = serializers.Charfield(source="group.id")
        rls = serializers.SerializerMethodField()  # 自定义显示
        x1 = MyField(source='username') # 自定义字段类
        
    
    • source

      source 一般是指定对应的模型字段,若指定,则序列化类属性可随意命名xxx

      • source指向的值,内部会判断其是否可执行,诸如callable(arg):

        是则加()执行:如get_user_type_display() 获取对应的用户类型中文

        否则取值: user_type 对应数据库存的数字 ;

      • source若指向的是外键字段,如group,则显示为一个object样子的字符串,

        可以通过如 group.id 取到关联的模型的字段值 ,内部会根据 ''." 来做切分,如果连续多个关联模型字段:group. x.y.z 则会一直取下去,最终再判断是否可执行

    • 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
      
    • 通过自定义字段类 来自定义字段显示 (不常用)

      class Myfield(serializers.CharField):
         def to_representation(self, value):
             print(value)
             return value
      

    ModelSerializer:

    class ModelUserSerializer(serializers.ModelSerializer):
        # 加额外字段
        ooo = serializer.CharField(source='get_user_type_display')
        
        class Meta:
            model = models.User
            # fields = "__all__"
            fields = ['username', 'pwd', 'ooo','group']
            # read_only_fields = ['user'] # 只用于显示 
            extra_kwargs = {'user':{'min_length':6},'pwd':{'validators': [PasswordValidator(666)]}} 
    
    • extra_kwargs

      表示为指定的序列化字段Field添加参数;一般在类中定义字段时直接传参

    2.1.2 深度序列化关联表

    当模型中有foreignkey 和 manytomany 这中关联外键字段时,使用 depth 参数可以对关联表进行深度序列化

    class ModelUserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.User
            # fields = "__all__"
            fields = ['username', 'pwd','group']
            depth = 1 # 序列化外键字段,展示关联表的所有字段值
    
    # depth = 0 表示只取当前模型的字段值,每加一层表示向下取关联模型的字段值
    # 推荐取值范围 0~5, 太深的连表操作影响效率
    

    可以根据模型的外键字段的id,在序列化返回的结果中再生成一个url,用于访问关联表的字段值

    class UserSerializer(serializers.ModelSerializer):
        group = serializers.Hypermedialink(view_name='gp',lookup_field='group_id',lookup_url_kwarg='pk')  # lookup_url_kwarg 默认pk
        
    class UserView(APIView):
        def get(self,request)
        	users = User.objects.all()
        	ser = UserSerializer(instance=users,many=True,context={'request':request})
        	return HttpReponse(json.dumps(ser.data,ensure_ascil=False))
    

    url配置:

    url(r'^group/(?P<pk>d+)$', views.GroupView.as_view(), name='gp')
    

    最后会根据group的id,反向生成一个url: group/1,类似于drf的通用试图List返回的数据那个 preurl 和nexturl

    2.2 源码流程

    class UserView(APIView):
        def get(self,request)
        	users = User.objects.all()
            # 单个对象,交给Serializer类处理  self.torepresentation
            # queryset,ListSerializer类处理  self.torepresentation
        	ser = UserSerializer(instance=users,many=True)
            ser.data
    

    通过ser.data可以往底层一步步查看序列化源码执行流程:

    # serializers.py: Serializer
    @property
    def data(self):
        ret = super(Serializer, self).data
        return ReturnDict(ret, serializer=self) #序列结果封装为有序字典
    
    @property
    def data(self):
        # 分支1:序列化展示
        self._data = self.to_representation(self.instance)
        # 分支2:反序列化校验
        self._data = self.to_representation(self.validated_data)
    
    def to_representation(self, instance):
        # 循环序列化器定义的所有Field
        for field in fields:
            attribute = field.get_attribute(instance)
    
    # fields.py: Field
    def get_attribute(self, instance):
        return get_attribute(instance, self.source_attrs)
    
    # self.source_attrs就是传入的source分割后的结果:[group,title]
    def bind(self, field_name, parent):
        # 该函数对我们传入的source做了一些操作
        if self.source is None:
        	self.source = field_name
    
        # self.source_attrs is a list of attributes that need to be looked up
        # when serializing the instance, or populating the validated data.
        if self.source == '*':
            self.source_attrs = []
        else:
            # 上面说到的按传入source进行分割后再去关联表取值
            self.source_attrs = self.source.split('.')
            
     def get_attribute(instance, attrs):
        if isinstance(instance, Mapping):
            instance = instance[attr]
        else:
            instance = getattr(instance, attr)
        ......
        #  如果为函数则加括号执行,如get_user_type_display
        if is_simple_callable(instance): 
        	instance = instance()
        
    

    3. drf反序列验证

    3.1 使用

    class XXValidator(object):
        def__init__(self, base):
            self.base = base
        
        def __call__(self, value):
            if not value.startswith(self.base):
                message = f'标题需以{self.base}开头'
                raise serializer.ValidationError(message)
        
        def set_context(self,serializer_field):
            # 执行验证之前调用,可不定义
            pass
    
    class UserGroupSerializer(serializers.Serializer):
        # error_message 自定义验证不通过的错误信息
        # validators 也可以指定方法
        title = serializer.CharField(error_message={'required':'标题不能为空'}, validators=[XXValidator('小屁孩')])
    

    自定义验证规则的几种方法

    • 使用 validators 参数,可使用类和函数
    • 方法 validate__字段名 ,单独验证某个字段
    • 方法 validate,验证所有字段

    方法中验证不通过抛异常:

    ​raise exceptions.ValidationError('error message')

    3.2 源码执行流程

    ser.is_valid() 会触发字段校验

    # serializers: BaseSerializer
    def is_valid(self, raise_exception=False):
        self.run_validation(self.initial_data)
    
    # fields: Field
    def run_validation(self, data=empty):
        # 先执行字段内部的验证,包括(字段本身正则,validate_xx钩子函数)
        value = self.to_internal_value(data)
        # 字段类中使用了validators参数的进一步验证
        self.run_validators(value)
    
    # serializers: Serializer
    def to_internal_value(self, data):
        ......
        for field in fields:
            # 获取自定义的 validate_字段 钩子方法
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            # 先字段本身的正则校验
            validated_value = field.run_validation(primitive_value)
            # 若validate_method有定义,调用 validate_字段 方法进一步校验
            validated_value = validate_method(validated_value)
    
    # fields: Field (先调用Serializer中run_validators,再调用其父类Field中的先调用Serializer中run_validators)
    def run_validators(self, value):
        # 循环调用传入字段的validators列表,validators=[XXValidator('小屁孩')
        for validator in self.validators:
        	validator.set_context(self)
        	# 调用validator进行校验
        	validator(value)
    

    分页

    1. 分页类型

    • a. 看第n页, 每页显示n条数据
    • b. 在第n个位置,向后查看n条数据(类似mysql的limit,offset)
    • c. 加密分页,上一页和下一页

    2. 分页时的问题

    一般做翻页时,每次扫描前面所有数据,取最后指定n条查看,越往后扫描越多,加载会越慢

    解决办法,只允许选择上一页和下一页:

    每次翻页时,取到记录的最大id和最小id,根据这个id直接跳到指定位置获取n条数据展示

    • 这种方式也可解决App,小程序中翻页数据顺序错乱的问题 (同一时刻大量用户插入记录,会导致翻页时可能新数据展示在旧数据之后)

    • 加密分页器 CursorPagination ,就是采取这种方式,数据量大时翻页效率快

    3. drf分页器

    3.1 drf内置3种分页类

    • PageNumberPagination : 看第n页, 每页显示n条数据

    • LimitOffsetPagination :在第n个位置,向后查看n条数据

    • CursorPagination :加密分页,只能上一页和下一页,内部根据当前页最大id,最小id来翻页,不用每次扫面前面所有记录;实现的部分源码如下:

      def paginate_queryset(self, queryset, request, view=None):
          if self.cursor.reverse != is_reversed:
              kwargs = {order_attr + '__lt': current_position}
          else:
      	  kwargs = {order_attr + '__gt': current_position}
      

    3.2 使用分页器

    class Pager1View(APIView):
        def get(self, request, *args, **kwargs):
            roles = Role.objects.all()
            # 使用其它分页类型只需替换分页器类即可
            pg = PageNumberPagination()
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
            ser = PageSerializer(instance=pager_roles,many=True)
            # return Response(ser)
            # 使用封装好分页的http响应
            return pg.get_paginated_response(ser.data)
    

    3.3自定义分页器

    • 默认的PageNumberPagination分页器无法调整每页显示条数,需配置page_size_query_param
    • CursorPagination 对分页页码做了加密,只能通过当前页来获取next页的页码,若使用get_paginated_response()将返回下一页的链接
      • url长这样:"/api/user/?cursor=cD02"
    # 继承PageNumberPagination
    class MyPagination(PageNumberPagination):
        page_size = 2  # 每页显示条数
        page_size_query_param = 'size'
        max_page_size = 5  # 每页最大显示条数
        page_query_param = 'page'
        
    # 继承LimitOffsetPagination
    class MyPagination(LimitOffsetPagination):
        default_limit = 2
        limit_query_param = 'limit'
        offset_query_param = 'offset'
        max_limit = 5
        
    # 继承CursorPagination 加密分页,
    class MyPagination(CursorPagination):
        cursor_query_param = 'cursor'
        page_size = 10
        invalid_cursor_message = _('Invalid cursor') # 错误页码返回信息
        ordering = '-id' # 根据指定字段排序,默认'-created'
        page_size_query_param = 'size'
        max_page_size = 20
    

    视图

    1. drf中可以继承的视图类

    • view : django的视图

    • APIView(View) : drf封装的基础视图

    • GenericAPIView(APIView) :多了几个方法,基本无意义

    • GenericViewSet(ViewSetMixin, generics.GenericAPIView) :路由配置上视图名需和请求方法对应:as_view({'get': 'list','post': 'create'})

    • ModelViewSet : 增删改查的视图集,继承如下

      ModelViewSet(mixins.CreateModelMixin,
                         mixins.RetrieveModelMixin,
                         mixins.UpdateModelMixin,
                         mixins.DestroyModelMixin,
                         mixins.ListModelMixin,
                         GenericViewSet)
      
      # 路由上需要对应两种路由,带id参数(查详情,删,改) 和 不带的(查列表,创建)
      
    • 增删改查也可单独继承,分开使用,都需继承核心GenericViewSet

    2.视图中的核心方法

    我们视图中常使用的方法,大都在views.py 和 generics.py 文件中

    generics.py:GenericAPIView

    def get_queryset(self)
        """返回queryset查寻集"""
    
    def get_object(self)
        """返回指定记录对象,默认根据id,可使用look_up参数配置"""
    
    def get_serializer(self, *args, **kwargs)
        """使用一个序列化器类创建对象返回"""
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
    	
    def get_serializer_class(self)
        """选择一个序列化器类,用它为’查询‘和’创建‘分配不同的序列化器"""
    
    def get_serializer_context(self)
        """封装一些参数,如request"""
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }
    
    def filter_queryset(self, queryset)
        """返回过滤后的queryset,配合filter过滤器使用"""
    
    def paginator(self)
        """返回一个分页对象"""
    	
    def paginate_queryset(self, queryset)
        """返回分页后的数据集queryset"""
        self.paginator.paginate_queryset(queryset, self.request, view=self)
    
    def get_paginated_response(self, data)
        """返回一个封装了分页信息(previous,next,count)的响应对象"""
    

    路由

    1. drf中的路由类

    • SimpleRouter

    • DefaultRouter

    使用视图集 ModelViewSet 时,可以自动生成路由

    from rest_framework import routers
    
    router = router.DefaultRouter()
    # 会自动生成五个url,列表('/xxx/'),详情('/xxx/?P<pk>d+/'),增('/xxx/'),删('/xxx/?P<pk>d+/'),改('/xxx/?P<pk>d+/')
    router.register(r'xxx', views.UserView) 
    
    urlpatterns = [
        url(r'^', include(router.urls))
    ]
    

    渲染器

    渲染器:控制返回响应数据的格式和显示样式

    1 drf中的渲染器

    • JSONRenderer : api接口返回给前端 json 形式的数据

    • BrowsableAPIRenderer : 浏览器直接访问视图时,展示的样式(我们经常看到的一个drf响应json数据的界面,可以去drf静态模板文件中【base.html】,改成自己喜欢的)

    • AdminRenderer:浏览器直接访问视图时,数据记录显示为一个表格样式

    • HTMLFormRenderer

    一般只需要使用 JSONRenderer 就行了,其他都是景上添花,无太大意义!!!

    配置渲染器后,在路由后面通过 format参数可使用对应样式,如 ?format=json或admin

    其它drf组件使用,及源码分析,后面再记录

  • 相关阅读:
    python os模块 常用命令
    将excel表格中的数据导入到SQL中
    临时数据表DataTable selected方法的使用
    ASP.NET数据库使用精典读取数据库中数据
    新兵开唱
    DropDownList控件的changed事件调用
    C# 读取Excel表格中的数据
    C# 强制类型转换示例
    用ADO.NET的ExecuteScalar方法返回单一值
    CentOS 6.3 桥接上网
  • 原文地址:https://www.cnblogs.com/Deaseyy/p/13698194.html
Copyright © 2011-2022 走看看