• rest_framework


    0. 安装模块

    pip install djangorestframework
    

    1. 基于CBV的认证

    1. APIView.dispatch方法

    • dispatch 是 request 请求的入口
    1. 对原生的 request 进行加工(丰富了一些功能),封装了 request 和 Basic对象list
    2. 获取原生的 request,使用 request._request
    3. 获取认证类对象request.authenticators
    import json
    from django.shortcuts import HttpResponse
    from rest_framework import exceptions
    from rest_framework.views import APIView
    
    class MyAuthentication(object):
        
        def authenticate(self, request):
            # 这里可以获取用户名和密码,用来认证
            token = request._request.GET.get('token')
            if not token:
                # 认证失败抛异常
                raise exceptions.AuthenticationFailed('用户认证失败')
            # 认证成功返回一个元组
            return ('henry', None)
        
        def authenticate_header(self, request):
            pass
    
    class Test(APIView):
        authentication_classes = [MyAuthentication, ]
    
        def get(self, request):
            print(request)
            return HttpResponse(json.dumps({'status': '200 ok', 'name': 'henry'}))
    
        def post(self, request):
            return HttpResponse('POST')
    

    2. 代码的流程

    1. dispatch执行流程

    1. request = self.initialize_request(request, *args, **kwargs)
      • 此时的 request 封装了原生的 requstauthenticators
      • request 本质是 Request 对象
      • authenticators=self.get_authenticators()
    2. self.initial(request, *args, **kwargs)
      • Runs anything that needs to occur prior to calling the method handler.
      • 实现:认证、权限、节流、版本等功能
      • 如:self.perform_authentication(request)

    2. 源码解读

    1. 获取原生的 request :request._request
    2. 获取认证类对象:request.authenticators
    # Provides an APIView class that is the base of all views in REST framework
    class APIView(View):
        def dispatch(self, request, *args, **kwargs):
          	...
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            ...
            try:
                self.initial(request, *args, **kwargs)   # 执行 用户、权限、节流 功能
                # 此时的 request 是封装后的,.method 调用的是 __getattr__ 方法
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                response = self.handle_exception(exc)
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
        
        def get_authenticators(self):
            return [auth() for auth in self.authentication_classes]
        # self.initial(request, *args, **kwargs)中调用
        def perform_authentication(self, request):
            request.user
    

    3. 登录

    from rest_framework.views import APIView
    from django.http import JsonResponse
    
    def md5(user):
        import hashlib, time
        m = hashlib.md5(bytes(user, encoding='utf8'))
        m.update(bytes(str(time.time()), encoding='utf8'))
        return m.hexdigest()
    
    class AuthView(APIView):
    
        def post(self, request):
            ret = {'code': 1000, 'msg': None}
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user, password=pwd)
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用户名或密码错误'
            else:
                # 为登录用户创建 token
                token = md5(user)
                # token 存在更新,不存在更新
                user_id = obj.first().id
    models.UserToken.objects.update_or_create(user_id=user_id, defaults={"token": token})
                ret['token'] = token
            return JsonResponse(ret)
    

    4. 认证访问

    1. request.user request.auth分别是验证类执行方法时返回的元组
    2. 如果验证类,没有返回值则交由下一个验证类进行处理,如果都没有返回值,则使用默认值:即AnonymousUser
    3. 如果是异常则抛:raise exceptions.AuthenticationFailed(ret)
    4. 认证一般不加多个
    data = {
        1: {'name': 'henry'},
        2: {'name': 'echo'},
    }
    class MyAuth:
    	
        def authenticate(self, request):
            ret = {'code': 1000, 'msg': None, 'data': None}
            token = request.GET.get('token')
            models.UserToken.objects.filter(token=token).first()
            if not token:
                ret['code'] = 1001
                ret['msg'] = '用户没有登录'
                raise exceptions.AuthenticationFailed(ret)
            # restful framework 内部会赋值给requst,供以后使用
            return token.user, token
        # 认证失败时,返回给浏览器的响应头
        def authenticate_header(self, request):
            pass
    
    class OrderView(APIView):
        # 需要认证的加上即可
        authentication_classes = [MyAuth, ]
        
        def get(self, request):
    
            ret = {'code': 1000, 'msg': None, 'data': None}
            try:
                ret['data'] = data
            except exceptions as e:
                pass
            return JsonResponse(ret)
    

    5. 认证的执行流程

    # apiview
    1. 执行dispatch()
    2. 执行 self.initialize_request(request, *args, **kwargs),封装了所有的认证类
    3. 执行 self.initial(request, *args, **kwargs)
    4. 执行 initial 中的 self.perform_authentication(request)执行request.user
    6. @property
       def user(self):
           ...
           self._authenticate()
    7. 执行 _authenticate(self),循环 self.authenticators 对象,执行 authenticate 的方法
    8. 执行自定义类中的 authenticate 的方法
    9. 执行视图函数
    

    6. 匿名用户配置

    • 推荐UNAUTHENTICATED_USER为None,token也为 None
    • DEFAULT_AUTHENTICATION_CLASSES:自定义默认验证的类,全局使用
    • 如果要某个类免除认证,则添加authentication_classes = []即可
    • 配置全局有效
    REST_FRAMEWORK = {
        # 认证
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.MyAuth'],
        'UNAUTHENTICATED_USER': lambda: '匿名用户',
        'UNAUTHENTICATED_TOKEN': lambda: '匿名用户的token',
    }
    

    7. 内置的认证类

    1. 内置类的用法

    • 为了规范认证类的写法,通常我们都继承 BaseAuthentication
    • 必须实现类中的两个方法
    from rest_framework.authentication import BaseAuthentication
    
    # 自定义的认证类
    class MyAuth(BaseAuthentication):
        def authenticate(self, request):
            ret = {'code': 1000, 'msg': None, 'data': None}
            token = request.GET.get('token')
            models.UserToken.objects.filter(token=token).first()
            if not token:
                ret['code'] = 1001
                ret['msg'] = '用户没有登录'
                raise exceptions.AuthenticationFailed(ret)
            # restful framework 内部会赋值给requst,供以后使用
            return token.user, token
        # 认证失败时,返回给浏览器的响应头
        def authenticate_header(self, request):
            pass
    

    2. BasicAuthentication

    • 跳出的用户名和密码如:FTP,浏览器提供的功能
    • 用户名和密码放在请求头中,会加密
    • 一种公认的认证方式

    2. 权限

    1. 基本使用

    class MyPermission():
        # 没有权限的提示信息
        message = 'xxxx'
        def has_permission(self, request, view):
            # 返回 True 则有权访问
            return False
              
    class OrderView(APIView):
        # 需要认证的加上即可
        authentication_classes = [MyAuth, ]
        # 返回 True 有权访问
        permission_classes = [MyPermission,]
        def get(self, request):
    		pass
    

    2. 配置文件

    REST_FRAMEWORK = {
        # 权限相关
        'DEFAULT_PERMISSION_CLASSES': ['app01.utils.Permissions.MyPermission']
    }
    

    3. 内置权限类

    • 为了规范认证类的写法,通常我们都继承 BasePermission
    • 返回True表示有权访问,False 表示无权访问,可以抛异常
    from rest_framework.permissions import BasePermission
    
    # 自定义的权限类
    class MyPermission(BasePermission):
        # 没有权限的提示信息
        message = 'xxxx'
        def has_permission(self, request, view):
            # 返回 True 则有权访问
            return False
    

    3. 节流(访问频率)

    1. 访问频率限制

    1. 不继承BaseThrottle

    import time
    
    class MyThrottle(object):
        VISIT_HIS = {}
    
        def __init__(self):
            self.history = None
    
        def allow_request(self, request, view):
            atime = time.time()
            remote_addr = request.META.get('REMOTE_ADDR')
            self.history = self.VISIT_HIS.get(remote_addr)
            if not self.history:
                self.VISIT_HIS[remote_addr] = [atime, ]
                return True
            while self.history and self.history[-1] < atime - 10:
                self.history.pop()
            if len(self.history) < 3:
                self.history.insert(0, atime)
                return True
    
        def wait(self):
            return 10 - time.time() + self.history[-1]
    

    2. 继承BaseThrottle

    import time
    from rest_framework.throttling import BaseThrottle
    class MyThrottle(BaseThrottle):
        VISIT_HIS = {}
    
        def __init__(self):
            self.history = None
    
        def allow_request(self, request, view):
            atime = time.time()
            remote_addr = self.get_ident(request)
            self.history = self.VISIT_HIS.get(remote_addr)
            if not self.history:
                self.VISIT_HIS[remote_addr] = [atime, ]
                return True
            while self.history and self.history[-1] < atime - 10:
                self.history.pop()
            if len(self.history) < 3:
                self.history.insert(0, atime)
                return True
    
        def wait(self):
            return 10 - time.time() + self.history[-1]
    

    3. 继承SimpleRateThrottle

    from rest_framework.throttling import SimpleRateThrottle
    # 针对匿名用户,使用 ip 地址作为标识
    class MyThrottle(SimpleRateThrottle):
        scope = 'any string'
    
        def get_cache_key(self, request, view):
            return self.get_ident(request)
    
    # 针对登录用户
    class UserThrottle(SimpleRateThrottle):
        scope = 'user'
    
        def get_cache_key(self, request, view):
            return request.user.username
    

    2. 配置文件

    REST_FRAMEWORK = {
        # 认证
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.Auth.MyAuth'],
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
    	# 权限
        'DEFAULT_PERMISSION_CLASSES': ['app01.utils.Permissions.MyPermission'],
        # 节流
        'DEFAULT_THROTTLE_CLASSES': ['app01.utils.Throttle.UserThrottle'],
    	# 设置 scope
        'DEFAULT_THROTTLE_RATES': {
            'any string': '3/m',
            'user':'10/m',
        }
    }
    

    3. 使用方式

    1. 局部使用

    class 类():	
        authentication_classes = [MyAuth,]
        permission_classes = [MyPermission,]
        throttle_classes = [MyThrottle, ]
        def ...
    

    2. 全局使用

    • 使用配置文件

    4. 版本*

    1. 版本

    • restful规定放置于:url 或者 请求头中

    2. 版本获取

    • 版本号在URL中:http://127.0.0.1:8000/api/users/?version=v1

    1. 自定义获取版本号

    class ParamVersion:
        def determine_version(self, request, *args, **kwargs):
            # reqeust.query_params.get('version')等价于 request._request.GET.get('version')
            version = request.query_params.get('version')
            return version
    
    class UserView(APIView):
        versioning_class = ParamVersion
    
        def get(self, request, *args, **kwargs):
            print(request.version)
            return HttpResponse('用户列表')
    

    2. 使用内置的类

    • 通过 versioning_class 表示局部使用
    • 一般通过配置文件进行全局配置,只要配置一份即可
    from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
    class UserView(APIView):
        # 版本:http://127.0.0.1:8000/api/users/?version=1
        # versioning_class = QueryParameterVersioning
        
        # 版本:http://127.0.0.1:8000/api/v1/users/
        # versioning_class = URLPathVersioning
         
        # 通过类获取 版本
        print(request.version)
        # url 方向解析
        url = request.versioning_scheme.reverse(viewname='user', request=request)
        print(url, 'here')
        # 使用django 的url 反向解析
        user_url = reverse(viewname='user', kwargs={'version': 'v2'})
        print('user_url: ', user_url)
        return HttpResponse('用户列表')
    
    • 路由系统
    # 项目的 url
    from django.conf.urls import url, include
    from django.contrib import admin
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^api/', include('app01.urls')),
    ]
    
    # app01
    from django.conf.urls import url
    from app01 import views
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='user'),
    ]
    
    • 配置文件
    REST_FRAMEWORK = {
        # 设置获取版本的类,这里使用 URLPathVersioning
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        'DEFAULT_VERSION': 'v1',
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        'VERSION_PARAM': 'version',
    }
    

    3. 源码获取版本流程

    • 完整的 intial 方法
    class APIView(View):
    	def initial(self, request, *args, **kwargs):
            """
            Runs anything that needs to occur prior to calling the method handler.
            """
            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)
            # 权限相关
            self.check_permissions(request)
            # 节流相关
            self.check_throttles(request)
        
        # 获取
    	def determine_version(self, request, *args, **kwargs):
            if self.versioning_class is None:
                return (None, None)
            scheme = self.versioning_class()
            # 执行version类中的 determine_version 方法,和 version类的对象
            return (scheme.determine_version(request, *args, **kwargs), scheme)
    

    5. 解析器*

    1. django的request.POST和body

    1. reqeust.POST有值的条件

    1. 如果请求头要求:Content-Type:applicatoin/x-www-form-urlencode,和请求数据格式必须是key:value & key :value...的格式,request.POST 才有值(request.body中解析数据)
      • form表单默认提交的数据请求头
      • ajax默认的请求头也符合这个要求
    2. 数据格式要求:name=henry&pwd=123

    2. POST中没有值

    • 可以从body中获取
    # 此时 request.POST中没有值,但可以从 request.body 中获取
    $.ajax({
        url:...,
        type:'POST',
        # 此时 request.POST中没有值
        headers:{Content-Type:'applicatoin/json',},
        # data 内部会转换为 name=henry&pwd=123 格式
        data:{
            name:'henry',
            pwd:123,
        }
    })
    

    2. resful_framework解析器

    • 使用时全局配置
    • 使用:request.data 取值时会触发解析

    1. 可以发json数据

    • 支持请求头:content-type:'applicatoin/json'
    • 支持数据:{'name': 'henry', 'pwd': 123}
    • 获取数据:reu
    from rest_framework.parsers import JSONParser
    
    class ParserView(APIView):
        """
        JSONParser 表示只能解析:Content-Type:applicatoin/json 的数据
        FormParser 表示只能解析:Content-Type:applicatoin/x-www-form-urlencode 的数据
        """
        parser_classes = [JSONParser, FormParser]
    
        def post(self, request, *args, **kwargs):
            # 获取解析后的结果
            print(request.POST)
            # print(request.body)
            print(request.data)
            return HttpResponse('ParserView')
    

    2. 处理流程

    1. 获取用户请求
    2. 获取用户请求体
    3. 根据请求头和 解析器支持的请求头进行比较,然后解析
    4. request.data

    3. 配置文件

    • settings.py
    REST_FRAMEWORK = {
        # 设置获取版本的类,这里使用 URLPathVersioning
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        'DEFAULT_VERSION': 'v1',
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        'VERSION_PARAM': 'version',
        # 设置使用的解析器
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser', 
            'rest_framework.parsers.FormParser'
        ]
    }
    

    3. 源码流程

    • 本质:根据content-type头进行数据解析
    1. 执行 request.data
    	@property
    	def data(self):
        	if not _hasattr(self, '_full_data'):
                self._load_data_and_files()
                return self._full_data
    2. 调用 _load_data_and_files() 方法,执行  self._parse()
    3. 执行选择 parser = self.negotiator.select_parser(self, self.parsers),解析器
    4. 调用解析器类parser对象的 parse 方法
    	parsed = parser.parse(stream, media_type, self.parser_context)
    5. 返回解析好的数据
    

    6. 序列化****

    1. 功能

    1. 请求数据的验证
    2. queryset进行序列化

    2. 序列化器类

    • models类
    from django.db import models
    
    class UserGroup(models.Model):
        title = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, '普通用户'),
            (2, 'VIP'),
            (3, 'SVIP'),
        )
        user_type = models.IntegerField(choices=user_type_choices)
        username = models.CharField(max_length=32, unique=True)
        password = models.CharField(max_length=64)
    	# 外键和多对多关系
        group = models.ForeignKey('UserGroup')
        role = models.ManyToManyField('Role')
    
    class UserToken(models.Model):
        user = models.OneToOneField(to='UserInfo')
        token = models.CharField(max_length=64)
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
    

    1. 简单使用

    • ser.data:表示序列化后的数据,ser是序列化器对象
    """序列化器"""
    from rest_framework import serializers
    
    class MySerializer(serializers.Serializer):
        title = serializers.CharField(max_length=32)
    
    # 视图类
    class RoleView(APIView):
        def get(self, request, *args, **kwargs):
            roles = models.Role.objects.all()
            # 方式一
            # roles = roles.values()
            # print(type(roles), roles)
            # roles = json.dumps(list(roles), ensure_ascii=False)
    
            # 方式二,针对多个对象,如果是单个对象则 many=False
            ser = MySerializer(instance=roles, many=True)
            ret = json.dumps(ser.data, ensure_ascii=False)
    
            return HttpResponse(ret)
    

    2. 特殊字段

    • choices、外键、多对多关系
    • 多对多关系:需要自定义方法,返回值为页面显示内容
    • RoleView视图类同上
    """序列化器"""
    class UserInfoSerializer(serializers.Serializer):
        username = serializers.CharField()
        password = serializers.CharField()
        # choices 选项
        user_type = serializers.CharField(source='get_user_type_display')
        # 外键
        group = serializers.CharField(source='group.title')
        # 多对对关系,自定义显示
        role = serializers.SerializerMethodField()
    
        # 自定义方法
        def get_role(self, row):
            row_obj_list = row.role.all()
            ret = []
            for i in row_obj_list:
                ret.append({'id': i.id, 'title': i.title})
            return ret
    

    3. 继承ModelSerializer

    """序列化器"""
    class UserInfoSerializer(serializers.ModelSerializer):
        # 增加其他字段,如果字段名和 model 类中相同,则覆盖
        group = serializers.CharField(source='group.title')
        user_type = serializers.CharField(source='get_user_type_display')
    
        class Meta:
            model = models.UserInfo
            fields = '__all__'
            # 表示深入到第几层,设置后不用重写 多对多和外键字段,建议 1-10
            depth = 1
    

    4. 自定义字段类

    # 自定义字段类
    class MyField(serializers.CharField):
        def to_presentation(self, value):
            print(value)
            return 'xxx'
    
    class UserInfoSerializer(serializers.ModelSerializer):
        # 增加其他字段,如果字段名和 model 类中相同,则覆盖
        group = serializers.CharField(source='group.title')
        
        # 生称 url,lookup_url_kwarg 是 url 中的参数
        # group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
        user_type = serializers.CharField(source='get_user_type_display')
        # 自定义字段
        xxx = MyField()
        class Meta:
            model = models.UserInfo
            fields = '__all__'
    
    • 查看group时,使用 url
    • lookup_field='group_id':表示被序列化数据表中的字段,lookup_url_kwarg是 url 中的参数
    • serializer实例化:必须加上 context={'request': request} 参数
     class GroupSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserGroup
            fields = '__all__'
    
    class GroupView(APIView):
    
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            groups = models.UserGroup.objects.filter(pk=pk)
            # 必须加上 context 参数
            ser = UserInfoSerializer(instance=userinfo, many=True, context={'request': request})
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
    

    2. url.py 配置

    urlpatterns = [
    	...
    	url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>d+)$', views.GroupView.as_view(), name='gp'),
    ]
    

    4. 结论

    1. 使用rest_framework提供的序列化器:先导入
    2. 创建自定义的序列化类,如果不指定source='字段名',属性名称必须为数据库中的字段名
    3. choices选项的字段:指定soucre=get_字段名_display,本质是通过通过执行函数获取,内部会判断(如果是可调用的则直接调用,不可调用的则直接返回),这里不需要加括号
    4. 多对多关系:使用 xxx = serializers.SerializerMethodField(),自定义显示,定义函数名为 get_xxx(self, row)
    5. 继承 ModelSerializer类时,只要使用了 depth = 1,自动化序列化,连表获取 多对多或外键 的数据
    6. 生成连接:group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')

    5. 源码流程

    1. 对象:交由 Serializer处理,如果是 QuerySet交由ListSerializer处理
    2. ser.data˙中调用:self.representation

    6. 请求数据校验

    1. 验证post数据

    • 使用postman提交数据,如过没有任何数据则会输出
      • {'title': [ErrorDetail(string='标题字段不能为空', code='required')]}
    • 有数据:OrderedDict([('title', 'test')])
    """验证功能"""
    # 自定义验证规则
    class XxValidator(object):
    
        def __init__(self, base):
            self.base = base
    
        def __call__(self, value, *args, **kwargs):
            if not value.startswith(self.base):
                message = 'This Filed must start with %s!' % self.base
                raise serializers.ValidationError(message)
    # 序列化器
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required': '标题字段不能为空'}, validators=[XxValidator('henry')])
    # 视图类
    class UserGroupView(APIView):
    
        def post(self, request, *args, **kwargs):
            ser = UserGroupSerializer(data=request.data)
            msg = '数据提交成功'
            if ser.is_valid():
                print(ser.validated_data['title'])
            else:
                msg = ser.errors
                print(msg)
            return HttpResponse('%s' % msg)
    

    7. 分页**

    1. 三类分页

    • 看第n页,每页显示n条数据
    • 在第n个位置,向后查看n条数据
    • 加密分页,只能看上一页和下一页

    2. 第一种分页

    1. 使用PageNumberPagination

    • utils目录下的,serializers.py
    # 序列化器
    from rest_framework import serializers
    from app01 import models
    
    class PageSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Role
            fields = '__all__'
    
    • 直接使用PageNumberPagination,默认不可以调整每页显示的个数,配置文件固定
    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination
    
    class Page1View(APIView):
        def get(self, request, *args, **kwargs):
            # 获取所有数据
            roles = models.Role.objects.all()
    		# 实例化 PageNumberPagination 类
            pg = PageNumberPagination()
            # 获取分页对象
            page_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
            print(page_roles)
            # 对分页数据仅进行序列化
            ser = PageSerializer(instance=page_roles, many=True)
            return Response(ser.data)
    
    • settings.py
    REST_FRAMEWORK = {
        ...,
        'PAGE_SIZE': 3,
    }
    
    • urls.py
    urlpatterns = [
       	...
        # 分页
        url(r'^(?P<version>[v1|v2]+)/page1/$', views.Page1View.as_view()),
    ]
    

    2. 自定义分页器

    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination
    
    class MyPagination(PageNumberPagination):
        # 每页显示个数
        page_size = 2
        # 通过page指定哪一页
        page_query_param = 'page'
        # 指定每页显示条数
        page_size_query_param = 'size'
        # 指定每页最大的数据量
        max_page_size = 5
    
    class Page1View(APIView):
        def get(self, request, *args, **kwargs):
            ...
            pg = MyPagination()
    		...
            # 带有上一页和下一页的链接
        	return pg.get_paginated_response(ser.data)
    

    2. 第二种分页

    1. 使用LimitOffsetPagination

    • 使用原生的分页器
    from rest_framework.pagination import LimitOffsetPagination
    
    class Page1View(APIView):
        def get(self, request, *args, **kwargs):
            ...
            pg = LimitOffsetPagination()
            ...
    

    2. 自定义分页

    class MyPagination(LimitOffsetPagination):
        # 默认一页显示数据量
        default_limit = 2
        # 指定一页显示数据量
        limit_query_param = 'limit'
        # 指定数据开始位置 + 1
        offset_query_param = 'offset'
        # 限定每页最多显示的数据
        max_limit = 6
    
    class Page1View(APIView):
        def get(self, request, *args, **kwargs):
            ...
            pg = MyPagination()
           	...
    

    3. 第三种分页

    1. 自定义分页器

    from rest_framework.response import Response
    from rest_framework.pagination import CursorPagination
    
    class MyPagination(CursorPagination):
    
        cursor_query_param = 'cursor'
        page_size = 2 
        # 排序规则
        ordering = 'id'
        page_size_query_param = 'size'
        max_page_size = 6
    
    class Page1View(APIView):
        def get(self, request, *args, **kwargs):
            ...
            pg = MyPagination()
           	...
    

    4. 总结

    1. 数据量大,如何分页?
      • 使用rest_framework中的分页器,显示上一页和下一页
      • 数据库性能,可以向restful中引出
    2. flask中使用 flask_restful组件
    from flask import Flask, request
    from flask_restful import Api, Resource
    
    app = Flask(__name__)
    api = Api(app)
    
    class UserAPI(Resource):
        def get(self, uid):
            return {'User': 'GET'}
    
        def put(self, uid):
            return {'User': 'PUT'}
    
        def delete(self, uid):
            return {'User': 'DELETE'}
    
        # 添加认证
        decorators = [auth.login_required]
    
    if __name__ == '__main__':
        app.run()
    
    • 绑定路由
    api.add_resource(UserAPI, '/users/<int:uid>', '/u/<int:uid>')
    

    8. 视图**

    1. GenericAPIView

    • class GenericAPIView(views.APIView):pass
    from rest_framework.response import Response
    from rest_framework.generics import GenericAPIView
    
    class View1View(GenericAPIView):
        queryset = models.Role.objects.all()
        serializer_class = PageSerializer
        pagination_class = PageNumberPagination
    
        def get(self, reqeust, *args, **kwargs):
            # 获取数据
            roles = self.get_queryset()
            # 获取分页的数据
            page_roles = self.paginate_queryset(roles)
            # 序列化
            ser = self.get_serializer(instance=page_roles, many=True)
            return Response(ser.data)
    

    2. GenericViewSet

    • 可以把获取多条数据和单条数据,通过 url 中的关系进行区分
    • class GenericViewSet(ViewSetMixin, generics.GenericAPIView):pass
    • 只是增加了方法名的映射,其他功能和GenericAPIView完全一样
    from rest_framework.viewsets import GenericViewSet
    
    class View2View(GenericViewSet):
    	...    
        def list(self, reqeust, *args, **kwargs):
            ...
            return Response(ser.data)
      
        def create(self, reqeust, *args, **kwargs):
            pass
    

    2. url.py

    urlpatterns = [
        # 视图
    	url(r'^(?P<version>[v1|v2]+)/view/$', views.ViewView.as_view({'get': 'list', 'post':'create'})),
    ]
    

    3. mixins系列

    • ListModelMixin:实现 list方法
    • CreateModelMixin:实现 create方法
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin, CreateModelMixin
    
    class ViewView(ListModelMixin, CreateModelMixin, GenericViewSet):
        queryset = models.Role.objects.all()
        serializer_class = PageSerializer
        pagination_class = PageNumberPagination
    

    4. ModelViewSet

    • modelviewset继承的类
    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        pass
    
    • 直接继承ModelViewSet
    from rest_framework.viewsets import ModelViewSet
    
    class ViewView(ModelViewSet):
        queryset = models.Role.objects.all()
        serializer_class = PageSerializer
        pagination_class = PageNumberPagination
    
    • urls.py
    urlpatterns = [
        ...
    	url(r'^(?P<version>[v1|v2]+)/view/$', views.ViewView.as_view({'get':'list',  'post':'create'})),
        url(r'^(?P<version>[v1|v2]+)/view/(?P<pk>d+)$',
            views.ViewView.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch':'partial_update'})),
    ]
    

    rest_framework 中类的继承关系

    9. 路由**

    1. 路由配置

    • 通过路由,区分不同格式的请求URL,响应不同格式的数据
    urlpatterns = [
        ...
        url(r'^(?P<version>[v1|v2]+)/view/$', views.ViewView.as_view({'get': 'list', 'post': 'create'})),
        url(r'^(?P<version>[v1|v2]+)/view/(?P<format>w+)/$', views.ViewView.as_view({'get': 'list', 'post': 'create'})),
        url(r'^(?P<version>[v1|v2]+)/view/(?P<pk>d+)/$', views.ViewView.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
        url(r'^(?P<version>[v1|v2]+)/view/(?P<pk>d+)/(?P<format>w+)/$', views.ViewView.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})),
    ]
    

    2. 自动生成路由

    from rest_framework import routers
    
    router = routers.DefaultRouter()
    router.register(r'xxx', views.ViewView)
    router.register(r'yyy', views.ViewView)
    urlpatterns = [
        ...
    	url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
    ]
    

    10. 渲染器*

    1. 注册app

    • settings.py
    INSTALLED_APPS = [
      	...,
        'rest_framework',
    ]
    
    • views.py
    """带渲染器"""
    from rest_framework.response import Response
    class Page1View(APIView):
    
        def get(self, request, *args, **kwargs):
            roles = models.Role.objects.all()
            ser = PageSerializer(instance=roles, many=True)
            print(ser.data)
            return Response(ser.data)
    

    2. 使用

    • 使用时,只要写renderer_classes = [JSONRenderer, BrowsableAPIRenderer]即可
    from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer, AdminRenderer
    
    class TestView(APIView):
    	# 写入配置文件
    	# renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
    
        def get(self, request, *args, **kwargs):
            roles = models.Role.objects.all()
            pg = PageNumberPagination()
            page_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
            print(page_roles)
            ser = PageSerializer(instance=page_roles, many=True)
            return Response(ser.data)
    
    • 配置文件
    REST_FRAMEWORK = {
        ...
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer', 
            'rest_framework.renderers.BrowsableAPIRenderer'
        ],
    }
    

    11. contenttype组件

    1. 作用

    • django内置的一个组件,帮助开发者做连表操作
    • 一张表和多张表中的数据同时关联时,需要使用

    2. 使用

    1. models.py

    • GenericForeignKey
    • GenericRelation
    from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
    from django.contrib.contenttypes.models import ContentType
    from django.db import models
    # 普通课程表
    class Course(models.Model):
        """普通课程"""
        title = models.CharField(max_length=32)
        # 仅用于反向查找,不会在数据库中生成
        price_policy_list = GenericRelation('PricePolicy')
    # 学位课程表
    class DegreeCourse(models.Model):
        """学位课程"""
        title = models.CharField(max_length=32)
        price_policy_list = GenericRelation('PricePolicy')
    # 价格策略表
    class PricePolicy(models.Model):
        price = models.IntegerField()
        period = models.IntegerField()
        # 具体 app 和 model 名称
        content_type = models.ForeignKey(ContentType, verbose_name='关联表名称')
        object_id = models.IntegerField(verbose_name='关联表数据ID')
        # 快速实现 content_type 操作
        content_obj = GenericForeignKey('content_type', 'object_id')
    

    2. views.py

    from django.http import HttpResponse
    from app01 import models
    
    def test(request):
        # 添加数据
        obj = models.DegreeCourse.objects.filter(title='python全栈').first()
        models.PricePolicy.objects.create(price=9.9, period=30, content_obj=obj)
    
        obj = models.DegreeCourse.objects.filter(title='python全栈').first()
        models.PricePolicy.objects.create(price=19.9, period=60, content_obj=obj)
    
        obj = models.DegreeCourse.objects.filter(title='python全栈').first()
        models.PricePolicy.objects.create(price=29.9, period=90, content_obj=obj)
    
        return HttpResponse('ok')
    
    

    3. 根据课程id获取课程

    • GenericRelation('PricePolicy')
    course = models.Course.objects.filter(id=1).first()
    # 获取所有课程对象 
    price_poicy = course.price_policy_list.all()
    
  • 相关阅读:
    快速掌握GIT
    Codeigniter+PHPExcel导出Excel文件
    git结构拓扑图
    (转)MVC新手指南
    (转)jQuery插件开发 其实很简单
    (转)Asp.net缓存简介
    (转)让ASP.NET MVC页面返回不同类型的内容
    (转)2010 .NET面试题整理之基础篇
    (转) .NET对象的XML序列化和反序列化
    (转)2010面试攻略
  • 原文地址:https://www.cnblogs.com/henryw/p/11712528.html
Copyright © 2020-2023  润新知