rest_framework
0. 安装模块
pip install djangorestframework
1. 基于CBV的认证
1. APIView.dispatch方法
- dispatch 是 request 请求的入口
 
- 对原生的 request 进行加工(丰富了一些功能),封装了 request 和 Basic对象list
 - 获取原生的 request,使用 
request._request - 获取认证类对象,
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执行流程
request = self.initialize_request(request, *args, **kwargs)- 此时的 request 封装了原生的 requst 和 authenticators
 - request 本质是 Request 对象
 - authenticators=self.get_authenticators()
 
self.initial(request, *args, **kwargs)- Runs anything that needs to occur prior to calling the method handler.
 - 实现:认证、权限、节流、版本等功能
 - 如:
self.perform_authentication(request) 
2. 源码解读
- 获取原生的 request :request._request
 - 获取认证类对象: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. 认证访问
request.user和request.auth分别是验证类执行方法时返回的元组- 如果验证类,没有返回值则交由下一个验证类进行处理,如果都没有返回值,则使用默认值:即AnonymousUser
 - 如果是异常则抛:
raise exceptions.AuthenticationFailed(ret) - 认证一般不加多个
 
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有值的条件
- 如果请求头要求:
Content-Type:applicatoin/x-www-form-urlencode,和请求数据格式必须是key:value & key :value...的格式,request.POST 才有值(request.body中解析数据)- form表单默认提交的数据请求头
 - ajax默认的请求头也符合这个要求
 
 - 数据格式要求:
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' - 支持数据:
 - 获取数据:
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. 处理流程
- 获取用户请求
 - 获取用户请求体
 - 根据请求头和 解析器支持的请求头进行比较,然后解析
 - 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. 功能
- 请求数据的验证
 - 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__'
3. 生成hypermedialink
1. 返回hypermedialink
- 查看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. 结论
- 使用rest_framework提供的序列化器:先导入
 - 创建自定义的序列化类,如果不指定
source='字段名',属性名称必须为数据库中的字段名 - choices选项的字段:指定
soucre=get_字段名_display,本质是通过通过执行函数获取,内部会判断(如果是可调用的则直接调用,不可调用的则直接返回),这里不需要加括号 - 多对多关系:使用
xxx = serializers.SerializerMethodField(),自定义显示,定义函数名为get_xxx(self, row) - 继承 
ModelSerializer类时,只要使用了depth = 1,自动化序列化,连表获取多对多或外键的数据 - 生成连接:
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk') 
5. 源码流程
- 对象:交由 
Serializer处理,如果是QuerySet交由ListSerializer处理 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. 总结
- 数据量大,如何分页?
- 使用
rest_framework中的分页器,显示上一页和下一页 - 数据库性能,可以向restful中引出
 
 - 使用
 - 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()
☞☞☞ 古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。 ☜ ☜ ☜

                
            
        
浙公网安备 33010602011771号