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组件使用,及源码分析,后面再记录

posted @ 2020-09-19 23:32  Deaseyy  阅读(896)  评论(0编辑  收藏  举报