django restfulwork 源码剖析

概要:

  1.restful 规范(建议);

  2. django rest framework框架

内容回顾:

  1.开发模式;

    - 普通开发模式(前后端放在一起写)

    - 前后端分离

      好处: 后端一套,前端使用app,pc等;

  2. 后端开发

    为前端提供URL(API的开发)

    注:永远返回Httpresponse

  3. django Fbv 和Cbv

一、Django CBV和FBV

在视图中,cbv支持四中method,如下:

from django.views import View
class StudentView(View):

    def get(self,request):
        return HttpResponse(‘GET‘)

    def post(self,request):
        return HttpResponse(‘POST‘)

    def put(self,request):
        return HttpResponse(‘POST‘)

    def delete(self,request):
        return HttpResponse(‘DELETE‘)

  

那么url必须这么写:

from app01 import views
urlpatterns = [
    url(r‘^students/$‘, views.StudentView.as_view()),
]

使用postman进行测试:

查看源码预备知识:封装

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Request(object):

    def __init__(self, obj):
        self.obj = obj

    @property
    def user(self):
        return self.obj.authticate()

class Auth(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def authticate(self):
        return self.name

class APIView(object):

    def dispatch(self):
        self.f2()

    def f2(self):
        a = Auth(‘charles‘, 18)
        req = Request(a)
        print(req.user)

obj = APIView()
obj.dispatch()

  

CBV实现原理: 在View类中有一个dispatch方法,在每个请求到达之后,会先执行,获取请求的method,然后通过反射,执行子类中对应的方法;

from django.views import View
class StudentView(View):

    def dispatch(self, request, *args, **kwargs):
        # return HttpResponse(‘dispatch‘)
        func = getattr(self,request.method.lower())
        ret = func(request, *args, **kwargs)
        return ret

    def get(self,request):
        return HttpResponse(‘GET‘)

    def post(self,request):
        return HttpResponse(‘POST‘)

    def put(self,request):
        return HttpResponse(‘POST‘)

    def delete(self,request):
        return HttpResponse(‘DELETE‘)

  

由上面的例子可以看到,dispatch方法是所有使用CBV的视图必须使用到的方法,为了避免每一个类都实现这个方法,可以通过类的继承,来避免代码的重复:

在下面的例子中,基类MyBaseView实现了一个dispatch方法,子类StudentView实现了继承了MyBaseView和View两个类,在实例化StudentView,并执行其方法的时候,会先在StudentView中寻找dispatch方法,如果没有,会去MyBaseView中去寻找,MyBaseView没有父类,所以会看是self是谁,然后从self的另外一个父类View中去寻找; 顺序是StudentView-->MyBaseView-->View

from django.views import View

class MyBaseView(object):
    def dispatch(self,request, *args, **kwargs):
        print(‘before‘)
        ret = super(MyBaseView, self).dispatch(request, *args, **kwargs)
        print(‘after‘)
        return ret

class StudentView(MyBaseView,View):

    def get(self,request,*args, **kwargs):
        return HttpResponse(‘GET‘)

    def post(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def put(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def delete(self,request,*args, **kwargs):
        return HttpResponse(‘DELETE‘)

  

二、Django中间件

1. 中间件执行顺序(中间件最多可以实现5个方法)

  正常顺序:  执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response

如果有报错: 执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response

如果视图函数有render:  执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response/process_render_template;

2. 使用中间件做过什么?

- 权限

  - 用户登录验证

  - django csrf token

  那么用户的csrf token是如何实现的?

     FBV:在django中,csrf token检测是在process_view方法中实现的,会检查视图是否使用@csrf_exempt,然后去请求体或者cookie中获取token;

       如果不使用中间件做csrf token认证,那么可以加@csrf_protect,对指定的实视图做验证;

     CBV: 有两种实现方法csrf_exempt

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

class StudentView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(StudentView,self).dispatch(request, *args, **kwargs)

    def get(self,request,*args, **kwargs):
        return HttpResponse(‘GET‘)

    def post(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def put(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def delete(self,request,*args, **kwargs):
        return HttpResponse(‘DELETE‘)
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt, name=‘dispatch‘)
class StudentView(View):
    def get(self,request,*args, **kwargs):
        return HttpResponse(‘GET‘)

    def post(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def put(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

  

三、restful 规范

1.根据method 不同,做不同的操作

url(r‘^order/$‘, views.order),

def order(request):
    if request.method == ‘GET‘:
        return HttpResponse(‘获取订单‘)
    elif request.method == ‘POST‘:
        return HttpResponse(‘创建订单‘)
    elif request.method == ‘PUT‘:
        return HttpResponse(‘更新订单‘)
    elif request.method == ‘DELETE‘:
        return HttpResponse(‘删除订单‘)

参考:https://www.cnblogs.com/wupeiqi/articles/7805382.html

四、restframework

使用自定义的类,实现API 认证; 具体看源码,和CBV 执行流程类似;

from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework import exceptions
class MyAuthenticate(object):

    def authenticate(self,request):
        token = request._request.GET.get(‘token‘)
        if not token:
            raise exceptions.AuthenticationFailed(‘用户认证失败‘)

    def authenticate_header(self, val):
        pass

class DogView(APIView):
    authentication_classes = [MyAuthenticate, ]

    def get(self,request,*args, **kwargs):
        ret = {
            ‘code‘: 1000,
            ‘msg‘: ‘xxx‘
        }
        return HttpResponse(json.dumps(ret),status=201)

    def post(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def put(self,request,*args, **kwargs):
        return HttpResponse(‘POST‘)

    def delete(self,request,*args, **kwargs):
        return HttpResponse(‘DELETE‘)  

需要掌握的内容:

1.中间件

2.CBV

3.csrf

4.规范

5.djangorestframework

- 如何验证(基于数据库实现用户认证)

-源码流程(面向对象回顾流程)

五、restframework之登录

问题: 有些API用户登录之后才可以访问,有些不需要用户登录;

先创建两张表

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)

class UserToken(models.Model):    # 存储用户登录成功之后的token
    user = models.OneToOneField(to=‘UserInfo‘)
    token = models.CharField(max_length=64)

编写API

url(r‘^api/v1/auth/$‘, views.AuthView.as_view())

from django.http import JsonResponse

# Create your views here.

from rest_framework.views import APIView
from api import models

def md5(user):
    import hashlib
    import time

    ctime = str(time.time())
    m = hashlib.md5(bytes(user, encoding=‘utf-8‘))
    m.update(bytes(ctime, encoding=‘utf-8‘))
    return m.hexdigest()

class AuthView(APIView):    """     用于用户登录认证    """

    def post(self, request, *args, **kwargs):
        ret = {‘code‘: 10000, ‘msg‘: None }
        try:
            user = request._request.POST.get(‘username‘)
            pwd = request._request.POST.get(‘password‘)
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret[‘code‘] = 1001
                ret[‘msg‘] = ‘用户名或密码错误‘
            # 为登录用户创建token
            token = md5(user)
            # 用户token存在就更新,不存在就创建
            models.UserToken.objects.update_or_create(user=obj, defaults={‘token‘: token})
            ret[‘token‘] = token    # 将token返回给用户
        except Exception as e:
            ret[‘code‘] = 1002
            ret[‘msg‘] = ‘请求异常‘
        return JsonResponse(ret)

使用postman发送请求进行验证

六、 rest framework之基于token实现基本用户认证

上面的例子是用户通过访问登录认证的API,获取返回的token,并将token存储到token表中;

用户拿到这个token之后,就可以访问其他的API了;

from rest_framework.views import APIView
from rest_framework.views import exceptions
from rest_framework.authentication import BaseAuthentication
from api import models

ORDER_DICT = {
    1: {
        ‘name‘: ‘charles‘,
        ‘age‘: 18,
        ‘gender‘: ‘男‘,
        ‘content‘: ‘....‘
    },
    2: {
        ‘name‘: ‘男‘,
        ‘age‘: 19,
        ‘gender‘: ‘男‘,
        ‘content‘: ‘......‘
    },
}

class Authtication(object):

    def authenticate(self, request):
        token = request._request.GET.get(‘token‘)
        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed(‘用户认证失败‘)
        # 在restframework内部会将整个两个字段赋值给request,以供后续继续使用
        return (token_obj.user, token_obj)

    def authenticate_header(self, request):
        pass

class OrderView(APIView):
    """
    订单相关业务
    """
    authentication_classes = [Authtication, ]   # 在访问API的时候,先走这个用户认证的类

    def get(self, request, *args, **kwargs):
        ret = {‘code‘: 1000, ‘msg‘: None, ‘data‘: None}
        # request.user --> token_obj.user
        # request.auth --> token_obj
        try:
            ret[‘data‘] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)

七、rest framework之认证基本流程源码分析

1、请求进来之后,会先执行dispatch()方法;

class OrderView(APIView):
    """
    订单相关业务
    """
    authentication_classes = [Authtication, ]

    def get(self, request, *args, **kwargs):
        self.dispatch()      # 使用pycharm进入dispatch方法,查看源码

2、先对request进行封装

self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)   # 对request进行封装,点击继续查看该方法

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        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):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]   # 通过列表生成式,执行self.authentication_classes方法,因为子类中没有这个方法,                                                                   那么会执行父类中的这个方法;  点击继续查看;

    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES   # 这个是存在于父类中的;  我们可以在自己的实例化的子类中使用自定义的类,替代这个类; 默认升是
BaseAuthentication
                                                                                                                                    

3、认证

self.initial(request, *args, **kwargs)    # 继续点击查看;

self.perform_authentication(request)  # 实现认证;

    def perform_authentication(self, request):
        request.user   # 执行request.user

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, ‘_user‘):
            with wrap_attributeerrors():
                # 获取认证对象,进行进一步认证
                self._authenticate()
        return self._user

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        # 循环所有authenticator对象
        for authenticator in self.authenticators:
            try:
                 # 执行authenticate方法
                 # 1.如果authenticate 方法抛出异常,self._not_authenticated执行
                 # 2. 没有抛出异常,有返回值,必须是元祖:(request.user, request.auth)
                 # 3. 返回None,我不管,下一个认证进行处理;
                 # 4.如果都返回None,执行self._not_authenticated(),返回(AnonymousUser, None)
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # request.user和request.auth
                self.user, self.auth = user_auth_tuple
                return

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None

        if api_settings.UNAUTHENTICATED_USER:     # AnonymousUser 匿名用户
            self.user = api_settings.UNAUTHENTICATED_USER()
        else:
            self.user = None

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()    # None
        else:
            self.auth = None

简要流程图如下:

八、rest framework之匿名用户配置

1.认证类的全局配置(全局使用)

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  # 认证类的默认配置

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

def reload_api_settings(*args, **kwargs):
    setting = kwargs[‘setting‘]
    if setting == ‘REST_FRAMEWORK‘:    # 获取settings的REST_FRAMEWORK 的配置项
        api_settings.reload()

在settings中定义 REST_FRAMEWORK 配置

REST_FRAMEWORK  =  {
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: [‘api.utils.auth.FirstAuthtication‘, ‘api.utils.auth.Authtication‘]
}

# 将认证的类,放入到上面配置的路径里面

# 视图函数中不包含上述的认证的类,并且要将登陆的API的authentication_classes设为空;
class AuthView(APIView):
    authentication_classes = []
    def post(self, request, *args, **kwargs):
            pass

class OrderView(APIView):
    """
    订单相关业务
    """

    def get(self, request, *args, **kwargs):
        ret = {‘code‘: 1000, ‘msg‘: None, ‘data‘: None}
        # request.user --> token_obj.user
        # request.auth --> token_obj
        try:
            ret[‘data‘] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)

2、在用户没有登录(匿名用户的时候)

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None

        if api_settings.UNAUTHENTICATED_USER:    # 获取settings中的用户默认用户是啥
            self.user = api_settings.UNAUTHENTICATED_USER()
        else:
            self.user = None

        if api_settings.UNAUTHENTICATED_TOKEN:    # 获取settings中的默认token是啥
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()
        else:
            self.auth = None

没有通过认证,默认为匿名用户

class UserInfo(APIView):

    authentication_classes = []
    def get(self,request,  *args, **kwargs):
        ret = {‘code‘: 1000, ‘msg‘: None, ‘data‘: None}
        print(request.user)
        return JsonResponse(ret

# 打印结果
AnonymousUser

# 在settings.py中增加如下配置,未登录时,用户和auth都为None,方面后续判断用户是否登录;
REST_FRAMEWORK  =  {
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: [‘api.utils.auth.FirstAuthtication‘, ‘api.utils.auth.Authtication‘],
    ‘UNAUTHENTICATED_USER‘:None,  #request.user
    ‘UNAUTHENTICATED_TOKEN‘:None  # request.auth
}

  

九、rest framework之内置基本认证

1.BaseAuthentication 基类,可以规范自定义的认证的类

from rest_framework.authentication import BaseAuthentication, BasicAuthentication

class FirstAuthtication(BaseAuthentication):

    def authenticate(self, request):
        pass

    def authenticate_header(self, request):
        pass

class Authtication(BaseAuthentication):

    def authenticate(self, request):
        token = request._request.GET.get(‘token‘)
        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed(‘用户认证失败‘)
        # 在restframework内部会将整个两个字段赋值给request,以供后续继续使用
        return (token_obj.user, token_obj)

    def authenticate_header(self, request):
        return ‘Basic realm="api"‘      # api信息加入请求头

2.其他认证   BasicAuthentication

十、rest framework之权限的基本使用

使用方法和自定义认证类非常类似

1.定义权限类

# 定义权限类
class MyPermisson(object):

    def has_permission(self, request, view):
        if request.user.user_type != 3:
            return False
        return True

class MyPermisson1(object):
    def has_permission(self, request, view):
        if request.user.user_type == 3:
            return False
        return True

2. 使用自定义类

class OrderView(APIView):
    """
    订单相关业务
    """
    permission_classes = [MyPermisson, ]

    def get(self, request, *args, **kwargs):
        ret = {‘code‘: 1000, ‘msg‘: None, ‘data‘: None}
        # request.user --> token_obj.user
        # request.auth --> token_obj

十一、 rest framework之权限源码流程

# 查看dispatch中检测权限的方法
 def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, ‘message‘, None)
                )

定义全局的权限检测类,并使用全局配置定义全局权限检测的类

class SVIPPermisson(object):
    message = ‘必须是SVIP才能访问‘
    def has_permission(self, request, view):
        if request.user.user_type != 3:
            return False
        return True

#定义配置
REST_FRAMEWORK  =  {
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: [‘api.utils.auth.FirstAuthtication‘, ‘api.utils.auth.Authtication‘],
    ‘UNAUTHENTICATED_USER‘:None,  #request.user
    ‘UNAUTHENTICATED_TOKEN‘:None,  # request.auth
    ‘DEFAULT_PERMISSION_CLASSES‘:[‘api.utils.permission.SVIPPermisson‘]  # request.auth
}

  

十二、rest framework之权限的内置类

django 内置的权限类在实际生成的环境不建议使用,但是建议继承,可以帮助规范自定义权限类的方法名称;

除了BasePermission之外,还有其他的类:

#实现代码:from rest_framework.permissions import BasePermission

class SVIPPermisson(BasePermission):
    message = ‘必须是SVIP才能访问‘
    def has_permission(self, request, view):
        if request.user.user_type != 3:
            return False
        return True

class MyPermisson1(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 3:
            return False
        return True

  

十三、rest framework之访问频率控制基本实现

import time

VISIT_RECODE = {}   # 放在全局变量中,重启之后,就会变空,可以放到缓存中
from rest_framework.throttling import BaseThrottle
class VisitThrottle(object):
    """60秒内只能访问3次"""
    def __init__(self):
        self.histoy = None

    def allow_request(self, request, view):
        # 1.获取用户IP
        remote_addr = request.META.get(‘REMOTE_ADDR‘)
        print(remote_addr)
        ctime = time.time()
        if remote_addr not in VISIT_RECODE:
            VISIT_RECODE[remote_addr] = [ctime, ]
            return True
        history = VISIT_RECODE.get(remote_addr)
        self.histoy = history
        while history and history[-1] < ctime -60:    # 如果记录的时间戳超过60s以内,就删除;
            history.pop()

        if len(history) < 3:
            history.insert(0, ctime)
            return True
        # return True  # 表示可以继续访问
        # return  False  # 表示访问频率太高,被限制

    def wait(self):
        """还有等多少秒才能访问 return 10 等10S才能访问"""
        ctime = time.time()
        return 60 - (ctime - self.histoy[-1])

class AuthView(APIView):
    authentication_classes = []
    throttle_classes = [VisitThrottle,]

十四、rest framework之访问频率控制源码流程

源码流程和上述认证与权限源码流程类似,下面使用全局配置类控制访问频率:

将上述代码挪到目录api.utils.thottle.VisitThrottle

REST_FRAMEWORK = {
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: [‘api.utils.auth.FirstAuthtication‘, ‘api.utils.auth.Authtication‘],
    ‘UNAUTHENTICATED_USER‘: None,  # request.user
    ‘UNAUTHENTICATED_TOKEN‘: None,  # request.auth
    ‘DEFAULT_PERMISSION_CLASSES‘:[‘api.utils.permission.SVIPPermisson‘],  # request.auth
    ‘DEFAULT_THROTTLE_CLASSES‘: [‘api.utils.thottle.VisitThrottle‘]   # 全局生效
}

十五、rest framework之基于内置类实现访问频率控制

实际上,内置的访问频率类已经实现了上述的方法,可以通过配置来自定义访问频率,VISIT_RECODE 是放置在缓存中的:

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    scope = ‘visit‘

    def get_cache_key(self, request, view):
        return self.get_ident(request)    # 获取用户IP

class UserThrottle(SimpleRateThrottle):
    scope = ‘user‘

    def get_cache_key(self, request, view):
        return request.user.username

###配置###
REST_FRAMEWORK = {
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: [‘api.utils.auth.FirstAuthtication‘, ‘api.utils.auth.Authtication‘],
    ‘UNAUTHENTICATED_USER‘: None,  # request.user
    ‘UNAUTHENTICATED_TOKEN‘: None,  # request.auth
    ‘DEFAULT_PERMISSION_CLASSES‘:[‘api.utils.permission.SVIPPermisson‘],  # request.auth
    ‘DEFAULT_THROTTLE_CLASSES‘: [‘api.utils.thottle.VisitThrottle‘],   #默认为对匿名用户做限制
    ‘DEFAULT_THROTTLE_RATES‘: {
        ‘visit‘: ‘3/m‘,     # 一分钟访问3次
        ‘user‘: ‘10/m‘
    }
}

###同时对登录用户做限制#####
from api.utils.thottle import UserThrottle
class OrderView(APIView):
    """
    订单相关业务
    """
    # permission_classes = [SVIPPermisson, ]
    throttle_classes = [UserThrottle, ]        # 只有用户登录才可以查看订单,使用另外一个访问频率限制类;

十六、版本

1、在url中通过GET传参:

使用自定义的类解析版本

class ParamVersion(object):
    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get(‘version‘)
        return version

class UserView(APIView):
    versioning_class = ParamVersion
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    def get(self, request, *args, **kwargs):
        print(request.version)
        return HttpResponse(‘用户列表‘)

#get请求如下: http://127.0.0.1:8080/api/users/?version=v3

使用内置的类解析版本参数,还可以通过配置定义默认的版本以及允许的版本:

from rest_framework.versioning import QueryParameterVersioning,

class ParamVersion(object):
    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get(‘version‘)
        return version

class UserView(APIView):
    versioning_class = QueryParameterVersioning
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    def get(self, request, *args, **kwargs):
        print(request.version)
        return HttpResponse(‘用户列表‘)

#####settings#####
REST_FRAMEWORK = {
    ‘DEFAULT_VERSION‘ : ‘v1‘,   # 默认的版本
    ‘ALLOWED_VERSIONS‘ : [‘v1‘, ‘v2‘],    # 允许请求的版本
    ‘VERSION_PARAM‘: ‘version‘,         # 版本的参数的key
}

2、在URL中传参(推荐使用): 版本在使用的时候,无需自定义,使用下面的方式就可以实现了;

urlpatterns = [
    url(r‘^(?P<version>[v1|v2]+)/users/$‘, views.UserView.as_view()),
]

from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
class UserView(APIView):
    versioning_class = URLPathVersioning   # 除了在这儿设置之外,还可以在配置中设置
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    def get(self, request, *args, **kwargs):
        print(request.version)
        return HttpResponse(‘用户列表‘)

###settings.py######在配置中设置
REST_FRAMEWORK = {
    ‘DEFAULT_VERSIONING_CLASS‘: ‘rest_framework.versioning.URLPathVersioning‘,
    ‘DEFAULT_VERSION‘ : ‘v1‘,
    ‘ALLOWED_VERSIONS‘ : [‘v1‘, ‘v2‘],
    ‘VERSION_PARAM‘: ‘version‘
}

十七、rest framework框架之版本源码

# 可以在视图中反向解析URL

from django.urls import reverse
class UserView(APIView):
    # versioning_class = ParamVersion
    # versioning_class = URLPathVersioning
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    def get(self, request, *args, **kwargs):
        # 1.获取版本
        print(request.version)
        # 2.获取处理版本的对象
        print(request.versioning_scheme)

        # 3.反向生成URL(REST FRAMEWORK)
        url1 = request.versioning_scheme.reverse(viewname=‘uuu‘, request=request)
        print(url1)

        # 4.反向生成URL
        url2 = reverse(viewname=‘uuu‘, kwargs={‘version‘: 2})
        print(url2)
        return HttpResponse(‘用户列表‘)

###打印结果
<rest_framework.versioning.URLPathVersioning object at 0x04335D50>
http://127.0.0.1:8080/api/v2/users/
/api/2/users/

十八、解析器

1.解析器预备知识(post提交的数据,会保存在request.body中,转换为QueryDict才能被request.post获取到)

#点击查看源码,
from  django.core.handlers.wsgi import  WSGIRequest
        elif self.content_type == ‘application/x-www-form-urlencoded‘:
            self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()   #

#如果想通过request.POST获取到post提交的数据,那么必须满足如下两个条件:
django:request.POST/ request.body
			1. 请求头要求:
				Content-Type: application/x-www-form-urlencoded
				PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。
			2. 数据格式要求:
				  name=charles&age=18&gender=男
# 如果不满足上述条件,那么就必须使用request.body将字节转换为str,然后再做解析:
			如:
				a. form表单提交,请求头和数据都满足上述条件:
					<form method...>
						input...

					</form>

				b. ajax提交
					$.ajax({
						url:...
						type:POST,
						data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
					})

					情况一:   #数据满足,请求头不满足
						$.ajax({
							url:...
							type:POST,
							headers:{‘Content-Type‘:"application/json"}
							data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
						})
						# body有值;POST无
					情况二:# 数据和请求头都不满足
						$.ajax({
							url:...
							type:POST,
							headers:{‘Content-Type‘:"application/json"}
							data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
						})
						# body有值;POST无
						# json.loads(request.body)

# rest framework 解析器

#JSONParser支持解析请求头为application/json的数据
#FormParser  支持解析请求头为content-type:application/x-www-form-urlencoded的数据

from rest_framework.parsers import JSONParser,FormParser
class ParserView(APIView):
    # parser_classes = [JSONParser,FormParser,]   #查看请求头,自动匹配解析器
    """
    JSONParser:表示只能解析content-type:application/json头
    JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头
    """

    def post(self, request, *args, **kwargs):
        """
        允许用户发送JSON格式数据
            a. content-type: application/json
            b. {‘name‘:‘alex‘,age:18}
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        """
        1. 获取用户请求
        2. 获取用户请求体
        3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
        4. JSONParser对象去请求体
        5. request.data
        """
        print(request.data)   # 解析后的数据

        return HttpResponse(‘ParserView‘)

  

 通过request.data可以看到解析器的源码,分析得到,解析器可以通过配置定义全局的解析器:

REST_FRAMEWORK = {
    ‘DEFAULT_PARSER_CLASSES‘ : [‘rest_framework.parsers.JSONParser‘, ‘rest_framework.parsers.FormParser‘]
}

使用非默认的解析器使用配置如下:
class ParserView(APIView):
    parser_classes = [JSONParser,FormParser,]   # 自己的视图类中使用的解析器
    """

#除了之外,还有如下的解析器:
class FormParser(BaseParser):
    """
    Parser for form data.
    """
    media_type = ‘application/x-www-form-urlencoded‘

    def parse(self, stream, media_type=None, parser_context=None):
        """
        Parses the incoming bytestream as a URL encoded form,
        and returns the resulting QueryDict.
        """
        parser_context = parser_context or {}
        encoding = parser_context.get(‘encoding‘, settings.DEFAULT_CHARSET)
        data = QueryDict(stream.read(), encoding=encoding)
        return data

class MultiPartParser(BaseParser):
    """
    Parser for multipart form data, which may include file data.
    """
    media_type = ‘multipart/form-data‘

    def parse(self, stream, media_type=None, parser_context=None):
        """
        Parses the incoming bytestream as a multipart encoded form,
        and returns a DataAndFiles object.

        `.data` will be a `QueryDict` containing all the form parameters.
        `.files` will be a `QueryDict` containing all the form files.
        """
        parser_context = parser_context or {}
        request = parser_context[‘request‘]
        encoding = parser_context.get(‘encoding‘, settings.DEFAULT_CHARSET)
        meta = request.META.copy()
        meta[‘CONTENT_TYPE‘] = media_type
        upload_handlers = request.upload_handlers

        try:
            parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
            data, files = parser.parse()
            return DataAndFiles(data, files)
        except MultiPartParserError as exc:
            raise ParseError(‘Multipart form parse error - %s‘ % six.text_type(exc))

class FileUploadParser(BaseParser):
    """
    Parser for file upload data.
    """
    media_type = ‘*/*‘
    errors = {
        ‘unhandled‘: ‘FileUpload parse error - none of upload handlers can handle the stream‘,
        ‘no_filename‘: ‘Missing filename. Request should include a Content-Disposition header with a filename parameter.‘,
    }

    def parse(self, stream, media_type=None, parser_context=None):
        """
        Treats the incoming bytestream as a raw file upload and returns
        a `DataAndFiles` object.

        `.data` will be None (we expect request body to be a file content).
        `.files` will be a `QueryDict` containing one ‘file‘ element.
        """
        parser_context = parser_context or {}
        request = parser_context[‘request‘]
        encoding = parser_context.get(‘encoding‘, settings.DEFAULT_CHARSET)
        meta = request.META
        upload_handlers = request.upload_handlers
        filename = self.get_filename(stream, media_type, parser_context)

        if not filename:
            raise ParseError(self.errors[‘no_filename‘])

        # Note that this code is extracted from Django‘s handling of
        # file uploads in MultiPartParser.
        content_type = meta.get(‘HTTP_CONTENT_TYPE‘,
                                meta.get(‘CONTENT_TYPE‘, ‘‘))
        try:
            content_length = int(meta.get(‘HTTP_CONTENT_LENGTH‘,
                                          meta.get(‘CONTENT_LENGTH‘, 0)))
        except (ValueError, TypeError):
            content_length = None

        # See if the handler will want to take care of the parsing.
        for handler in upload_handlers:
            result = handler.handle_raw_input(stream,
                                              meta,
                                              content_length,
                                              None,
                                              encoding)
            if result is not None:
                return DataAndFiles({}, {‘file‘: result[1]})

        # This is the standard case.
        possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size]
        chunk_size = min([2 ** 31 - 4] + possible_sizes)
        chunks = ChunkIter(stream, chunk_size)
        counters = [0] * len(upload_handlers)

        for index, handler in enumerate(upload_handlers):
            try:
                handler.new_file(None, filename, content_type,
                                 content_length, encoding)
            except StopFutureHandlers:
                upload_handlers = upload_handlers[:index + 1]
                break

        for chunk in chunks:
            for index, handler in enumerate(upload_handlers):
                chunk_length = len(chunk)
                chunk = handler.receive_data_chunk(chunk, counters[index])
                counters[index] += chunk_length
                if chunk is None:
                    break

        for index, handler in enumerate(upload_handlers):
            file_obj = handler.file_complete(counters[index])
            if file_obj is not None:
                return DataAndFiles({}, {‘file‘: file_obj})

        raise ParseError(self.errors[‘unhandled‘])

    def get_filename(self, stream, media_type, parser_context):
        """
        Detects the uploaded file name. First searches a ‘filename‘ url kwarg.
        Then tries to parse Content-Disposition header.
        """
        try:
            return parser_context[‘kwargs‘][‘filename‘]
        except KeyError:
            pass

        try:
            meta = parser_context[‘request‘].META
            disposition = parse_header(meta[‘HTTP_CONTENT_DISPOSITION‘].encode(‘utf-8‘))
            filename_parm = disposition[1]
            if ‘filename*‘ in filename_parm:
                return self.get_encoded_filename(filename_parm)
            return force_text(filename_parm[‘filename‘])
        except (AttributeError, KeyError, ValueError):
            pass

    def get_encoded_filename(self, filename_parm):
        """
        Handle encoded filenames per RFC6266. See also:
        http://tools.ietf.org/html/rfc2231#section-4
        """
        encoded_filename = force_text(filename_parm[‘filename*‘])
        try:
            charset, lang, filename = encoded_filename.split(‘\‘‘, 2)
            filename = urlparse.unquote(filename)
        except (ValueError, LookupError):
            filename = force_text(filename_parm[‘filename‘])
        return filename

引申内容如下:

1. http 状态码
2. http请求方法
3. http 请求头

十九、 rest framework框架之序列化

数据库表结构如下:

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)
    group = models.ForeignKey(‘UserGroup‘)

    username = models.CharField(max_length=32, unique=True)
    password = models.CharField(max_length=64)

    roles = 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、序列化基本使用

a. django的序列化

如果是django的QuerySet对象,直接使用json.dumps进行处理,是会报错的,使用django的序列化工具不太好用,一版我们使用values/value_list转换为列表之后,再进行序列化:

import json
class RoleView(APIView):
    def get(self, request, *args, **kwargs):
        roles = models.Role.objects.all().values(‘id‘, ‘title‘)
        roles = list(roles)
        ret = json.dumps(roles, ensure_ascii=False)   # ensure_ascii=False 表示如果有中文,不是输出字节码,而是中文字符
        return HttpResponse(ret)

 

b.使用rest framework的序列化工具

b1.

from rest_framework import serializers

class RolesSerializer(serializers.Serializer):   # 下面的字段必须是数据库的字段
    id = serializers.IntegerField()
    title = serializers.CharField()

class RoleView(APIView):
    def get(self, request, *args, **kwargs):
        roles = models.Role.objects.all()
        ser = RolesSerializer(instance=roles, many=True)   # 如果QuerySet不是一个对象,使用many=True,如果是一个对象,如.first()/.last(),那么使用many=False
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)  

b2.

上述序列化的是简单的CharField字典,如果字段是choice/ForeignKey/ManyToMany,那么如何序列化呢?

class UserInfoSerializer(serializers.Serializer):
    xxxx = serializers.CharField(source=‘user_type‘)  # 显示choice的id
    ooo = serializers.CharField(source=‘get_user_type_display‘)   # 显示choice的value
    username = serializers.CharField()
    password = serializers.CharField()
    gp = serializers.CharField(source=‘group.title‘)    # source指定序列化的字段

    # rls = serializers.CharField(source=‘roles.all‘)
    rls  = serializers.SerializerMethodField()     # ManyToMany 可以指定方法,由方法返回需要被序列化展示的内容

    def get_rls(self, row):     # 方法名为get_名称(这里是rls)
        roles_obj_list = row.roles.all()
        ret = []
        for item in roles_obj_list:
            ret.append({‘id‘: item.id, ‘title‘: item.title})
        return ret

class UserInfoView(APIView):

    def get(self, request, *args, **kwargs):

        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users, many=True)

        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)

参考: http://www.cnblogs.com/wupeiqi/articles/7805382.html

b3.

使用rest framework   ModelSerializer也可以使用上述的序列化的功能,但是更省事:

class UserInfoSerializer(serializers.ModelSerializer):
    ooo = serializers.CharField(source=‘get_user_type_display‘)
    rls = serializers.SerializerMethodField()

    class Meta:
        model = models.UserInfo
        # fields = "__all__"   # 显示所有字段,但是外键部分只显示ID
        fields = [‘id‘, ‘username‘, ‘password‘, ‘ooo‘, ‘rls‘, ‘group‘]

    def get_rls(self, row):
        roles_obj_list = row.roles.all()
        ret = []
        for item in roles_obj_list:
            ret.append({‘id‘: item.id, ‘title‘: item.title})
        return ret

在序列化的时候,上面的CharField可以使用自定义的类(一般不使用):

class MyField(serializers.CharField):
    pass

class UserInfoSerializer(serializers.ModelSerializer):
    ooo = serializers.CharField(source=‘get_user_type_display‘)
    rls = serializers.SerializerMethodField()
    x1 = MyField(source=‘username‘)

    class Meta:
        model = models.UserInfo
        # fields = "__all__"   # 显示所有字段,但是外键部分只显示ID
        fields = [‘id‘, ‘username‘, ‘password‘, ‘ooo‘, ‘rls‘, ‘group‘, ‘x1‘]

    def get_rls(self, row):
        roles_obj_list = row.roles.all()
        ret = []
        for item in roles_obj_list:
            ret.append({‘id‘: item.id, ‘title‘: item.title})
        return ret

class MyField(serializers.CharField):

    def to_representation(self, value):
        print(value)
        return ‘xxxxx‘     # 返回值, 这里将返回显示的值写死了,而不是从数据库中去获取

b4.

使用depth, 可以自动序列化连表

class UserInfoSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserInfo
        # fields = "__all__"   # 显示所有字段,但是外键部分只显示ID
        fields = [‘id‘, ‘username‘, ‘password‘, ‘roles‘, ‘group‘]
        depth = 1   # 建议值为0~3,默认为0

b5.

自动生成链接

urls.py

urlpatterns = [
    url(r‘^(?P<version>[v1|v2]+)/userinfo/$‘, views.UserInfoView.as_view()),
    url(r‘^(?P<version>[v1|v2]+)/usergroup/(?P<xxx>\d+)$‘, views.UserGroupView.as_view(), name=‘gp‘),
]

views.py

class UserInfoSerializer(serializers.ModelSerializer):
    group = serializers.HyperlinkedIdentityField(view_name=‘gp‘, lookup_field=‘group_id‘, lookup_url_kwarg=‘xxx‘)    #lookup_field 从数据库取值
    class Meta:
        model = models.UserInfo
        fields = [‘id‘, ‘username‘, ‘password‘, ‘roles‘, ‘group‘]
        depth = 0

class UserInfoView(APIView):

    def get(self, request, *args, **kwargs):

        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users, many=True, context={‘request‘: request})

        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)

class UserGroupSerializer(serializers.ModelSerializer): 

    class Meta:
        model = models.UserGroup
        fields = "__all__"   # 显示所有字段,但是外键部分只显示ID
        # fields = [‘id‘, ‘username‘, ‘password‘, ‘roles‘, ‘group‘]
        # depth = 0

class UserGroupView(APIView):

    def get(self, request, *args, **kwargs):
        pk = kwargs.get(‘xxx‘)
        obj = models.UserGroup.objects.filter(pk=pk).first()
        print(obj)
        ser = UserGroupSerializer(instance=obj, many=False)

        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret) 

引申知识点: 如何判断一个变量是否是函数

import types
def func(arg):
    # if callable(arg):
    if isinstance(arg, types.FunctionType):
        print(arg())
    else:
        print(arg)

func(123)
func(lambda :"666")

二十、验证用户请求数据

这里我们使用的解析器是: [‘rest_framework.parsers.JSONParser‘, ‘rest_framework.parsers.FormParser‘]

所以提交的验证数据为:

class XXValidator(object):
    def __init__(self, base):
        self.base = base

    def __call__(self, value,  *args, **kwargs):   # 这里的value是用户提交的数据
        if not value.startswith(self.base):
            message = ‘标题必须以 %s 开头‘ %self.base
            raise serializers.ValidationError(message)

    def set_context(self, seralizer_field):
        pass
class GroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={‘required‘: ‘标题不能为空‘}, validators=[XXValidator(‘老男人‘)])    # validator表示自定义验证规则, 

class GroupView(APIView):

    def post(self, request, *args, **kwargs):

        ser = GroupSerializer(data=request.data)    # request.data 获取请求体中的数据
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)            # 输出   {‘title‘: [‘标题必须以 老男人 开头‘]}
        return HttpResponse(‘提交数据‘)

 验证钩子

class GroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={‘required‘: ‘标题不能为空‘}, validators=[XXValidator(‘老男人‘)])

    def validate_title(self, value):   # 这里的value是验证的消息,是ser.validated_data,数据通过验证,会走这个钩子函数
        from rest_framework import exceptions
        # raise exceptions.ValidationError(‘哈哈哈‘)
        print(value, "xxxxx")
        return value

二十一、渲染器

from api.utils.serializers.pager import PagerSerializer
from rest_framework.response import Response
class Pager1View(APIView):

    def get(self, request, *args, **kwargs):

        roles = models.Role.objects.all()
        ser = PagerSerializer(instance=roles, many=True)
        return Response(ser.data)    # 使用渲染器显示接口数据

为什么会展示上面的内容呢?

from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer   # 默认使用的渲染器是这里的全部的渲染器

		class TestView(APIView):
			# renderer_classes = [JSONRenderer,BrowsableAPIRenderer]    # 可以在这里定义该视图使用的渲染器,
			def get(self, request, *args, **kwargs):
				# 获取所有数据
				roles = models.Role.objects.all()

				# 创建分页对象
				# pg = CursorPagination()
				pg = MyCursorPagination()

				# 在数据库中获取分页的数据
				pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)

				# 对数据进行序列化
				ser = PagerSerialiser(instance=pager_roles, many=True)

				return Response(ser.data)

# 也可以使用全局的配置,配置默认的渲染器
		REST_FRAMEWORK = {
			"DEFAULT_RENDERER_CLASSES":[
				‘rest_framework.renderers.JSONRenderer‘,
				‘rest_framework.renderers.BrowsableAPIRenderer‘,
			]
		}

当然,我们可以继承上面的渲染器,然后自定制自己的显示页面等内容:

class BrowsableAPIRenderer(BaseRenderer):
    """
    HTML renderer used to self-document the API.
    """
    media_type = ‘text/html‘
    format = ‘api‘
    template = ‘rest_framework/api.html‘     # 这里页面的内容,我们可以进行在子类中替换,哈哈哈哈
    filter_template = ‘rest_framework/filters/base.html‘
    code_style = ‘emacs‘
    charset = ‘utf-8‘
    form_renderer_class = HTMLFormRenderer

  

二十二、分页器

#自定义序列化的类

#pager.py
from rest_framework import serializers
from  api import models

class PagerSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        fields = "__all__"

22.1

#分页

REST_FRAMEWORK = {
    ‘PAGE_SIZE‘: 2,     # 定义每页分页的大小
}

class Pager1View(APIView):

    def get(self, request, *args, **kwargs):

        #获取所有数据
        roles = models.Role.objects.all()

        # 创建分页对象
        pg = PageNumberPagination()

        # 在数据库中获取分页的数据
        pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)   # 返回的是分页的对象

        # 对分页的数据进行序列化
        ser = PagerSerializer(instance=pager_roles, many=True)
        print(pager_roles)
        return Response(ser.data)

22.2

除此之外,我们还可以自定义分页的大小,通过自定义的类来实现:

class MyPageNumberPagination(PageNumberPagination):
    page_size = 2
    page_query_param = ‘page‘    # 查询分页时使用的参数
    page_size_query_param = ‘size‘  # 是否可以自定义查询分页的大小

    max_page_size = 5  # 每个分页的最大值

class Pager1View(APIView):

    def get(self, request, *args, **kwargs):

        #获取所有数据
        roles = models.Role.objects.all()

        # 创建分页对象
        pg = MyPageNumberPagination()   # 自定义的分页类

        # 在数据库中获取分页的数据
        pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)

        # 对分页的数据进行序列化
        ser = PagerSerializer(instance=pager_roles, many=True)
        print(pager_roles)
        return Response(ser.data)

22.3

如果返回为:

        ser = PagerSerializer(instance=pager_roles, many=True)
        print(pager_roles)
        # return Response(ser.data)
        return pg.get_paginated_response(ser.data)

则显示如下的内容:

22.4

另外使用LimitOffsetPagination也可以实现上述功能

from rest_framework.pagination import LimitOffsetPagination
class LimitOffsetPagination(BasePagination):
    """
    A limit/offset based style. For example:

    http://api.example.org/accounts/?limit=100
    http://api.example.org/accounts/?offset=400&limit=100    # offset 是从0开始的
    """
    default_limit = api_settings.PAGE_SIZE
    limit_query_param = ‘limit‘
    limit_query_description = _(‘Number of results to return per page.‘)
    offset_query_param = ‘offset‘
    offset_query_description = _(‘The initial index from which to return the results.‘)
    max_limit = None
    template = ‘rest_framework/pagination/numbers.html‘

22.5 加密分页

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class MyPageNumberPagination(CursorPagination):
    cursor_query_param = ‘cursor‘    # 查询页的ID
    page_size = 2
    ordering = ‘-id‘     # 排序
    page_size_query_param = None
    max_page_size = None

二十三、 rest framework之视图

23.1  GenericAPIView

从源码看,GenericAPIView是继承了APIView, 实现的功能和APIView没有任何区别,做了解即可:

继承的顺序是View-->APIView--> GenericView

class APIView(View):

    # The following policies ma

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.

实现代码如下:

from rest_framework.pagination import PageNumberPagination
from api.utils.serializers.pager import PagerSerializer
from rest_framework.generics import GenericAPIView
class View1View(GenericAPIView):
    queryset = models.Role.objects.all()
    serializer_class = PagerSerializer
    pagination_class = PageNumberPagination
    def get(self, request, *args, **kwargs):
        # 获取数据
        roles = self.get_queryset()   # models.Role.objects.all()
        pager_roles = self.paginate_queryset(roles)

        # 序列化
        ser = self.get_serializer(instance=pager_roles, many=True)
        return Response(ser.data)

23.2 GenericViewSet

与上面的GenericAPIView不同的是,重写了as_view()方法;

# 继承了ViewSetMixin和GenericAPIView两个类,ViewSetMixin中重写了as_view方法;

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

class ViewSetMixin(object):
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the ‘GET‘ and ‘POST‘ methods
    to the ‘list‘ and ‘create‘ actions...

    view = MyViewSet.as_view({‘get‘: ‘list‘, ‘post‘: ‘create‘})
    """

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        """
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        """
        # The suffix initkwarg is reserved for displaying the viewset type.
        # eg. ‘List‘ or ‘Instance‘.
        cls.suffix = None

        # Setting a basename allows a view to reverse its action urls. This
        # value is provided by the router through the initkwargs.
        cls.basename = None

实现代码:

urls.py

url(r‘^(?P<version>[v1|v2]+)/v1/$‘, views.View1View.as_view({‘get‘: ‘list‘})),   # method为GET时,去寻找视图类中的list方法;

views.py

from rest_framework.viewsets import GenericViewSet
class View1View(GenericViewSet):
    queryset = models.Role.objects.all()
    serializer_class = PagerSerializer
    pagination_class = PageNumberPagination
    def list(self, request, *args, **kwargs):   # list方法必须要实现
        # 获取数据
        roles = self.get_queryset()   # models.Role.objects.all()
        pager_roles = self.paginate_queryset(roles)

        # 序列化
        ser = self.get_serializer(instance=pager_roles, many=True)
        return Response(ser.data) 

23.3 ModelViewSet

ModelViewSet 继承了多个类: 每个类实现了一个特定的方法,在实现的时候,无需在视图中实现这些方法,只需要在as_view中指定方法名就可以了

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

实现代码:

urls.py

 url(r‘^(?P<version>[v1|v2]+)/v1/$‘, views.View1View.as_view({‘get‘: ‘list‘, ‘post‘:‘create‘})),    # 获取列表和创建数据无需传递id
    url(r‘^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)$‘, views.View1View.as_view({‘get‘: ‘retrieve‘, ‘delete‘: ‘destroy‘, ‘put‘: ‘update‘, ‘patch‘: ‘partial_update‘})),    # 因为update、delete等操作,需要传递id,所以在实现的时候,需要两个路由;

views.py

from rest_framework.viewsets import ModelViewSet
class View1View(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = PagerSerializer
    pagination_class = PageNumberPagination

当然了,也可以只继承mixins中的任意一个或者多个类;

总结使用:

			a. 增删改查  使用ModelViewSet
			b. 增删     使用CreateModelMixin,DestroyModelMixin  GenericViewSet
			c. 复杂逻辑  使用GenericViewSet 或 APIView

  

二十四、路由

一般一个视图,我们最多写四个url就够了

# http://127.0.0.1:8000/api/v1/v1/?format=json
url(r‘^(?P<version>[v1|v2]+)/v1/$‘, views.View1View.as_view({‘get‘: ‘list‘,‘post‘:‘create‘})),
# http://127.0.0.1:8000/api/v1/v1.json
url(r‘^(?P<version>[v1|v2]+)/v1\.(?P<format>\w+)$‘, views.View1View.as_view({‘get‘: ‘list‘,‘post‘:‘create‘})),
url(r‘^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$‘, views.View1View.as_view({‘get‘: ‘retrieve‘,‘delete‘:‘destroy‘,‘put‘:‘update‘,‘patch‘:‘partial_update‘})),
url(r‘^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)\.(?P<format>\w+)$‘, views.View1View.as_view({‘get‘: ‘retrieve‘,‘delete‘:‘destroy‘,‘put‘:‘update‘,‘patch‘:‘partial_update‘})),

  

如果嫌麻烦,可以使用全自动路由:

from api import views
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r‘xxxxx‘, views.View1View)
router.register(r‘rt‘, views.View1View)

urlpatterns = [
    url(r‘^(?P<version>[v1|v2]+)/‘, include(router.urls)),
]

  

如果写单个url就自己写,如果写全部的增删改查就自动生成;

二十五、content-type

在做前后端分离的时候,涉及跨域的问题,解决办法:

1. jsonp

2. cors:  - 将这个响应头放在中间件中进行实现;

content-type: 是django内置的组件,帮助开发者做连表操作。

from django.db import models

# Create your models here.

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation

class Course(models.Model):
    """
    普通课程
    """
    title = models.CharField(max_length=12)
    # 仅用于反向查找
    price_policy_list = GenericRelation("PricePlicy")

class DegreeCourse(models.Model):
    """
    学位课程
    """
    title = models.CharField(max_length=32)
    price_policy_list = GenericRelation("PricePlicy")

class PricePlicy(models.Model):
    """
    价格策略
    """
    price = models.IntegerField()
    period = models.IntegerField()

    content_type = models.ForeignKey(ContentType, verbose_name=‘关联的表的名称‘)
    object_id = models.IntegerField(verbose_name=‘关联的表中的数据行的ID‘)
    # 快速实现content_type 操作
    content_object = GenericForeignKey(‘content_type‘, ‘object_id‘)   # 会自动找到obj 对象的id和关联的表中的数据行的ID,并进行赋值

obj = DegreeCourse.objects.filter(title=‘python‘).first()
PricePlicy.objects.create(price=9.9, period=30, content_object=obj)

 

视图函数

from app01 import models

def test(request):
    obj1 = models.DegreeCourse.objects.filter(title=‘python‘).first()
    models.PricePlicy.objects.create(price=9.9, period=30, content_object=obj1)

    obj1 = models.Course.objects.filter(title=‘rest framework‘).first()
    models.PricePlicy.objects.create(price=9.9, period=30, content_object=obj1)
    course = models.Course.objects.filter(id=1).first()
    price_policys = course.price_policy_list.all()
    print(price_policys)
    return HttpResponse(‘....‘)

 

原文地址:https://www.cnblogs.com/cqq-20151202/p/8735217.html

时间: 2024-10-29 21:52:50

django restfulwork 源码剖析的相关文章

Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax csrf相关装饰器 在CBV上加csrf装饰器 Django settings源码剖析及模仿使用 Django settings源码剖析 查看内部配置文件 模仿使用 Auth模块 auth简介 auth模块常用方法 创建用户 校验用户名和密码 保存用户登录状态 判断当前用户是否登录 校验原密码 修改密

Django Rest Framework源码剖析(二)-----权限

一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时候需要对权限进行控制.下面将介绍DRF的权限控制源码剖析. 二.基本使用 这里继续使用之前的示例,加入相应的权限,这里先介绍使用示例,然后在分析权限源码 1.在django 项目下新建立目录utils,并建立permissions.py,添加权限控制: class MyPremission(obje

Django Rest Framework源码剖析(三)-----频率控制

一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过代理限制访问频率等,但是django rest framework自身就提供了访问频率的控制,可以从代码本身做控制. 二.频率控制内部原理概述 django rest framework 中频率控制基本原理基于访问次数和时间,通过计算实现,当然我们也可以自己定义频率控制方法.基本原理如下: 启用频率

Django Rest Framework源码剖析(八)-----视图与路由

一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使用者而言不同的视图具有不同的功能,这样我们可以根据需求定制自己视图.以下是官网传送门:http://www.django-rest-framework.org/api-guide/views/ 在之前的文章中,由于参杂了权限.认证等(如果不了解请看博客的以前的文章),但在本章中基本可以不使用,所进使

源码剖析Django REST framework的认证方式及自定义认证

源码剖析Django REST framework的认证方式 由Django的CBV模式流程,可以知道在url匹配完成后,会执行自定义的类中的as_view方法. 如果自定义的类中没有定义as_view方法,根据面向对象中类的继承可以知道,则会执行其父类View中的as_view方法 在Django的View的as_view方法中,又会调用dispatch方法. 现在来看看Django restframework的认证流程 Django restframework是基于Django的框架,所以基

下载-深入浅出Netty源码剖析、Netty实战高性能分布式RPC、NIO+Netty5各种RPC架构实战演练三部曲视频教程

下载-深入浅出Netty源码剖析.Netty实战高性能分布式RPC.NIO+Netty5各种RPC架构实战演练三部曲视频教程 第一部分:入浅出Netty源码剖析 第二部分:Netty实战高性能分布式RPC 第三部分:NIO+Netty5各种RPC架构实战演练

Phaser实现源码剖析

在这里首先说明一下,由于Phaser在4.3代码里是存在,但并没有被开放出来供使用,但已经被本人大致研究了,因此也一并进行剖析. Phaser是一个可以重复利用的同步栅栏,功能上与CyclicBarrier和CountDownLatch相似,不过提供更加灵活的用法.也就是说,Phaser的同步模型与它们差不多.一般运用的场景是一组线程希望同时到达某个执行点后(先到达的会被阻塞),执行一个指定任务,然后这些线程才被唤醒继续执行其它任务. Phaser一般是定义一个parties数(parties一

【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,

转:【Java集合源码剖析】Vector源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35793865   Vector简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. Vector没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实