DjangoRestFrameWork

安装:pip install djangorestframework

restful规范

DRF 配置文件

REST_FRAMEWORK = {
    # 解析器配置
	'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
        'rest_framework.parsers.FormParser'
        'rest_framework.parsers.MultiPartParser'
    ]

	# 版本配置
	'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
    'DEFAULT_VERSION': 'v1',				# 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],		# 允许的版本
    'VERSION_PARAM': 'version' 				# URL中获取值的key


    # 用户登录认证
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    "DEFAULT_AUTHENTICATION_CLASSES": ["web.utils.TestAuthentication",],

    # 用户权限验证
    "DEFAULT_PERMISSION_CLASSES": ["web.utils.TestPermission",],

    # 用户访问频率限制(匿名时用IP限制+登录时用Token限制)
    'DEFAULT_THROTTLE_CLASSES': [
        'api.utils.throttles.throttles.LuffyAnonRateThrottle',
        'api.utils.throttles.throttles.LuffyUserRateThrottle',
    ],
    
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/day',
        'user': '10/day',
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

DRF 认证模块

1、token认证

# urls.py ---------------------------------------------------------------------------------------------------
from django.conf.urls import url, include
from web.viewsimport TestView
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

# views.py --------------------------------------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions
token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]
class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")
            return ('登录用户', '用户token')
    def authenticate_header(self, request):
        # 验证失败时,返回的响应头WWW-Authenticate对应的值
        pass

class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')	

2、请求头认证

# urls.py
from django.conf.urls import url, include
from web.viewsimport TestView
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

# views.py
import base64
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions
token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]
class TestAuthentication(BaseAuthentication):
	def authenticate(self, request):
		auth = request.META.get('HTTP_AUTHORIZATION', b'')
        if auth:
			auth = auth.encode('utf-8')
		auth = auth.split()
		if not auth or auth[0].lower() != b'basic':
			raise exceptions.AuthenticationFailed('验证失败')
		username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
        if username == 'alex' and password == '123':
            return ('登录用户', '用户token')
        else:
            raise exceptions.AuthenticationFailed('用户名或密码错误')
	def authenticate_header(self, request):
		return 'Basic realm=api'

class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []
    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

3、多个认证规则

# urls.py
from django.conf.urls import url, include
from web.views.s2_auth import TestView
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

# views.py
import base64
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions
token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]
class Test1Authentication(BaseAuthentication):
    def authenticate(self, request):
        auth = request.META.get('HTTP_AUTHORIZATION', b'')
        if auth:
            auth = auth.encode('utf-8')
        else:
            return None

        auth = auth.split()
        if not auth or auth[0].lower() != b'basic':
             raise exceptions.AuthenticationFailed('验证失败')
        if len(auth) != 2:
             raise exceptions.AuthenticationFailed('验证失败')
                
        username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
        if username == 'alex' and password == '123':
            return ('登录用户', '用户token')
        else:
            raise exceptions.AuthenticationFailed('用户名或密码错误')
            
	def authenticate_header(self, request):
        pass

class Test2Authentication(BaseAuthentication):
    def authenticate(self, request):
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")
            return ('登录用户', '用户token')

        def authenticate_header(self, request):
            pass

class TestView(APIView):
    authentication_classes = [Test1Authentication, Test2Authentication]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

4、认证和权限

# urls.py
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
			
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission
from rest_framework.request import Request
from rest_framework import exceptions
token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]
class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用户认证失败")
            return ('登录用户', '用户token')

        def authenticate_header(self, request):
            pass

class TestPermission(BasePermission):
    message = "权限验证失败"
    def has_permission(self, request, view):
        """
		判断是否有权限访问当前请求
		Return `True` if permission is granted, `False` otherwise.
		:param request: 
		:param view: 
		:return: True有权限;False无权限
		"""
        if request.user == "管理员":
            return True

    # GenericAPIView中get_object时调用
    def has_object_permission(self, request, view, obj):
        """
        视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :param obj: 
        :return: True有权限;False无权限
        """
        if request.user == "管理员":
        	return True


class TestView(APIView):
    # 认证的动作是由request.user触发
    authentication_classes = [TestAuthentication, ]
    # 权限
    # 循环执行所有的权限
    permission_classes = [TestPermission, ]
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

        def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

        def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

5、全局使用

# settings.py
REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
    	"web.utils.TestAuthentication",
	],
    "DEFAULT_PERMISSION_CLASSES": [
        "web.utils.TestPermission",
    ],
}

# urls.py
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view()),
]

# views.py:
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    def put(self, request, *args, **kwargs):
   		return Response('PUT请求,响应内容')

6、json web token

安装:pip install pyjwt

(1) jwt_auth.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import jwt
import datetime
from jwt import exceptions

JWT_SALT = '6#1hi$%5*+(uwbp+#x1@k9873v63xl-n2kw#6z%@=u6ifis8ds'


def create_token(payload, timeout=60):
    """
    :param payload:  例如:{'user_id':1,'username':'wupeiqi'}用户信息
    :param timeout: token的过期时间,默认20分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
    result = jwt.encode(payload=payload, key=JWT_SALT, algorithm="HS256", headers=headers).decode('utf-8')
    return result


def parse_payload(token):
    """
    对token进行和发行校验并获取payload
    :param token:
    :return:
    """
    result = {'status': False, 'data': None, 'error': None}
    try:
        verified_payload = jwt.decode(token, JWT_SALT, True)
        result['status'] = True
        result['data'] = verified_payload
    except exceptions.ExpiredSignatureError:
        result['error'] = 'token已失效'
    except jwt.DecodeError:
        result['error'] = 'token认证失败'
    except jwt.InvalidTokenError:
        result['error'] = '非法的token'
    return result

(2) authentication.py

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from utils.jwt_auth import parse_payload

# A:Json Web Token 认证
# 方式一
# http://www.pythonav.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
class JwtQueryParamAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token')
        payload = parse_payload(token)
        if not payload['status']:
            raise exceptions.AuthenticationFailed(payload)

        # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
        return (payload, token)


# 方式二
# Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU
class JwtAuthorizationAuthentication(BaseAuthentication):
    def authenticate(self, request):

        # 非登录页面需要校验token
        authorization = request.META.get('HTTP_AUTHORIZATION', '')
        auth = authorization.split()
        if not auth:
            raise exceptions.AuthenticationFailed({'code': 302, 'error': '未获取到Authorization请求头', 'status': False})
        if auth[0].lower() != 'jwt':
            raise exceptions.AuthenticationFailed({'code': 302, 'error': 'Authorization请求头中认证方式错误', 'status': False})
        if len(auth) == 1:
            raise exceptions.AuthenticationFailed({'code': 302, 'error': "非法Authorization请求头", 'status': False})
        elif len(auth) > 2:
            raise exceptions.AuthenticationFailed({'code': 302, 'error': "非法Authorization请求头", 'status': False})

        token = auth[1]
        result = parse_payload(token)
        if not result['status']:
            raise exceptions.AuthenticationFailed(result)

        # 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
        return (result, token)

(3) views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from utils import authentication
from utils.jwt_auth import create_token

# 登录接口
class LoginView(APIView):
    throttle_classes = [AnonRateThrottle, ]

    def post(self, request):
        response_data = {"code": 200, "msg": "已成功登录!", "data": None, "token": None}
        # 账号
        username = request.data["username"]
        # 密码
        password = request.data["password"]
        if not all([username, password]):
            response_data["code"] = 400
            response_data["msg"] = "必填项不能为空"
        else:
            user_obj = models.User.objects.filter(username=username, password=password).first()
            if user_obj:
                token = create_token({'token': user_obj.username})
                response_data["token"] = "jwt " + token
                serializer = api_serializer.UserBaseSerializer(user_obj)
                response_data["data"] = serializer.data
            else:
                response_data["code"] = 400
                response_data["msg"] = "账号或密码错误,请重试"
        return Response(data=response_data)

        
class TestView(APIView):
    # 认证的动作是由request.user触发
    authentication_classes = [authentication.JwtAuthorizationAuthentication, ]
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')

        def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')

        def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

DRF 权限模块

1、基本使用

(1) Permission.py

from api_v1 import models

class TestPermission(object):
    def has_permission(self, request, view):
        print(request.user)
        role = request.user["data"]["role"]
        if 'A' == role["user__role"]:
            return True
        return False

(2) view.py

from django.shortcuts import render

from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from utils.Authentication import JwtAuthorizationAuthentication
from utils.Pagination import AuthorsCursorPagination
from utils.Permission import TestPermission
from . import serializer
from api_v1 import models


# 后台用户管理
class UserView(ModelViewSet):
    queryset = models.UserModel.objects.filter(status=True)
    authentication_classes = [JwtAuthorizationAuthentication, ]
    serializer_class = serializer.UserSerializer
    pagination_class = AuthorsCursorPagination
    permission_classes = [TestPermission,]
    ordering_fields = ('id',)

    # 查看用户列表
    def list(self, request, *args, **kwargs):
        response_data = {"code": status.HTTP_200_OK, "msg": "success", "data": None}
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        serializer = self.get_serializer(page, many=True)
        response = self.get_paginated_response(serializer.data)
        response_data["data"] = response.data
        return Response(response_data)

    # 创建用户
    def create(self, request, *args, **kwargs):
        pass

    # 修改用户
    def update(self, request, *args, **kwargs):
        pass

    # 查看用户详情
    def retrieve(self, request, *args, **kwargs):
        pass

    # 删除
    def destroy(self, request, *args, **kwargs):
        pass

(3) 全局配置

# django rest framework配置
REST_FRAMEWORK = {
    # 用户登录认证
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    "DEFAULT_AUTHENTICATION_CLASSES": [],
    # 用户权限验证
    "DEFAULT_PERMISSION_CLASSES": [utils.Premission.TestPremission],
}

2、内置权限

(1) BasePermission

class BasePermissionMetaclass(OperationHolderMixin, type):
    pass

class BasePermission(metaclass=BasePermissionMetaclass):
    """
    A base class from which all permission classes should inherit.
    """
    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

(2) 继承内置类

from rest_framework.permissions import BasePermission

class TestPermission(BasePermission):
    def has_permission(self, request, view):
        print(request.user)
        role = request.user["data"]["role"]
        if 'A' == role["user__role"]:
            return True
        return False

3、总结

使用:

  1. 类,继承 BasePermission
  2. 必须实现 has_permission 方法
  3. 返回值 True(有权访问), False(无权访问)

源码:

​ ......

DRF 节流限制

1、基于用户IP限制访问频率

# urls.py ====================================================
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

# views.py ====================================================
import time
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework.throttling import BaseThrottle
from rest_framework.settings import api_settings
# 保存访问记录
RECORD = {
    '用户IP': [12312139, 12312135, 12312133, ]
}

class TestThrottle(BaseThrottle):
    ctime = time.time
    def get_ident(self, request):
        """
		根据用户IP和代理IP,当做请求者的唯一标识
		"""
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES
        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()
        return ''.join(xff.split()) if xff else remote_addr

    def allow_request(self, request, view):
        """
		是否仍然在允许范围内
		Return `True` if the request should be allowed, `False` otherwise.
		:param request: 
		:param view: 
		:return: True,表示可以通过;False表示已超过限制,不允许访问
		"""
        
        # 获取用户唯一标识(如:IP)
        # 允许一分钟访问10次
        num_request = 10
        time_request = 60
        now = self.ctime()
        ident = self.get_ident(request)
        self.ident = ident
        if ident not in RECORD:
            RECORD[ident] = [now, ]
            return True
        history = RECORD[ident]
        while history and history[-1] <= now - time_request:
            history.pop()
            if len(history) < num_request:
                history.insert(0, now)
                return True

	def wait(self):
		"""
		多少秒后可以允许继续访问
		Optionally, return a recommended number of seconds to wait before
		the next request.
		"""
        last_time = RECORD[self.ident][0]
        now = self.ctime()
        return int(60 + last_time - now)
    
class TestView(APIView):
    throttle_classes = [TestThrottle, ]
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')
    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')
    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
    def throttled(self, request, wait):
        """
		访问次数被限制时,定制错误信息
		"""
        class Throttled(exceptions.Throttled):
            default_detail = '请求被限制.'
            extra_detail_singular = '请 {wait} 秒之后再重试.'
            extra_detail_plural = '请 {wait} 秒之后再重试.'
            raise Throttled(wait)

2、基于用户IP限制访问频率

# settings.py ---------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
    	'test_scope': '10/m',
    },
}

# urls.py -------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view()),
]

# views.py -----------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework.throttling import SimpleRateThrottle
class TestThrottle(SimpleRateThrottle):
# 配置文件定义的显示频率的Key
    scope = "test_scope"
    def get_cache_key(self, request, view):
        if not request.user:
        	ident = self.get_ident(request)
        else:
            ident = request.user
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }
            
class TestView(APIView):
    throttle_classes = [TestThrottle, ]
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')

    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

    def throttled(self, request, wait):
        """
        访问次数被限制时,定制错误信息
        """
        class Throttled(exceptions.Throttled):
            default_detail = '请求被限制.'
            extra_detail_singular = '请 {wait} 秒之后再重试.'
            extra_detail_plural = '请 {wait} 秒之后再重试.'
            raise Throttled(wait)

3、view中限制请求频率

# settings.py -------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'xxxxxx': '10/m',
    },
}

# urls.py -----------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework.throttling import ScopedRateThrottle

# 继承 ScopedRateThrottle
class TestThrottle(ScopedRateThrottle):
    def get_cache_key(self, request, view):
        """
        应该返回一个惟一的缓存键,该缓存键可用于节流。
        必须覆盖。
        如果请求不应该被节流,可以返回“None”。
        """
        if not request.user:
        	ident = self.get_ident(request)
        else:
            ident = request.user

            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }

class TestView(APIView):
    throttle_classes = [TestThrottle, ]
    # 在settings中获取 xxxxxx 对应的频率限制值
    throttle_scope = "xxxxxx"
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
        return Response('POST请求,响应内容')
        
    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')
        
    def throttled(self, request, wait):
        """
        访问次数被限制时,定制错误信息
        """
        class Throttled(exceptions.Throttled):
            default_detail = '请求被限制.'
            extra_detail_singular = '请 {wait} 秒之后再重试.'
            extra_detail_plural = '请 {wait} 秒之后再重试.'
            raise Throttled(wait)

4、匿名时用IP限制+登录时用Token限制

# settings.py ---------------------------------------------------------
REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_THROTTLE_RATES': {
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

# urls.py ------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s3_throttling import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view()),
]

# views.py ----------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import SimpleRateThrottle
class LuffyAnonRateThrottle(SimpleRateThrottle):
    """
    匿名用户,根据IP进行限制
    """
    scope = "luffy_anon"
    def get_cache_key(self, request, view):
        # 用户已登录,则跳过 匿名频率限制
        if request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }

class LuffyUserRateThrottle(SimpleRateThrottle):
    """
    登录用户,根据用户token限制
    """
    scope = "luffy_user"

    def get_ident(self, request):
        """
        认证成功时:request.user是用户对象;request.auth是token对象
        :param request: 
        :return: 
        """
        # return request.auth.token
        return "user_token"

    def get_cache_key(self, request, view):
        """
        获取缓存key
        :param request: 
        :param view: 
        :return: 
        """
        # 未登录用户,则跳过 Token限制
        if not request.user:
        	return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }

class TestView(APIView):
    throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]
    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

5、全局使用

# settings.py ------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'api.utils.throttles.throttles.LuffyAnonRateThrottle',
        'api.utils.throttles.throttles.LuffyUserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/day',
        'user': '10/day',
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

DRF 版本控制

1、基于url的get传参方式

如:/users?version=v1

# settings.py ----------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
    'VERSION_PARAM': 'version'          # URL中获取值的key
}

# urls.py ----------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view(),name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning
class TestView(APIView):
    versioning_class = QueryParameterVersioning
    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

2、基于url的正则方式

如:/v1/users/

# settings.py -------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
    'VERSION_PARAM': 'version'          # URL中获取值的key
}

# urls.py -----------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]

# views.py ----------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning

class TestView(APIView):
    versioning_class = URLPathVersioning
    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

3、基于 accept 请求头方式

如:Accept: application/json; version=1.0

# settings.py --------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
    'VERSION_PARAM': 'version'          # URL中获取值的key
}

# urls.py ---------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning
class TestView(APIView):
    versioning_class = AcceptHeaderVersioning
    def get(self, request, *args, **kwargs):
        # 获取版本 HTTP_ACCEPT头
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

4、基于主机名方法

如:v1.example.com

# settings.py --------------------------------------------------------------
ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
    'VERSION_PARAM': 'version'  # URL中获取值的key
}

# urls.py -------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'^test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning
class TestView(APIView):
    versioning_class = HostNameVersioning
    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

5、基于django路由系统的namespace

如:example.com/v1/users/

# settings.py -----------------------------------------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
    'VERSION_PARAM': 'version'  # URL中获取值的key
}

# urls.py ------------------------------------------------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
    url(r'^v1/', ([
    	url(r'test/', TestView.as_view(), name='test'),
    ], None, 'v1')),
    
    url(r'^v2/', ([
    	url(r'test/', TestView.as_view(), name='test'),
    ], None, 'v2')),
]

# views.py --------------------------------------------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning
class TestView(APIView):
    versioning_class = NamespaceVersioning
    def get(self, request, *args, **kwargs):
        # 获取版本
        print(request.version)
        # 获取版本管理的类
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)
        return Response('GET请求,响应内容')
        
    def post(self, request, *args, **kwargs):
    	return Response('POST请求,响应内容')
    	
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

6、全局使用

# settings.py ------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
    'DEFAULT_VERSION': 'v1',
    'ALLOWED_VERSIONS': ['v1', 'v2'],
    'VERSION_PARAM': 'version' 
}

DRF 解析器

1、application/json

仅处理请求头content-type为application/json的请求体

# urls.py -----------------------------------------------------------
from django.conf.urls import url, include
from web.views.s5_parser import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py ----------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser
class TestView(APIView):
    parser_classes = [JSONParser, ]
    def post(self, request, *args, **kwargs):
        print(request.content_type)
        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')
    
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

2、application/x-www-form-urlencoded

仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser
class TestView(APIView):
    parser_classes = [FormParser, ]
    def post(self, request, *args, **kwargs):
        print(request.content_type)
        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

3、multipart/form-data

仅处理请求头content-type为multipart/form-data的请求体

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser
class TestView(APIView):
    parser_classes = [MultiPartParser, ]
    def post(self, request, *args, **kwargs):
        print(request.content_type)
        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')
        
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

# upload.html --------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">
    <input type="submit" value="提交">
</form>
</body>
</html>

4、仅上传文件

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser
class TestView(APIView):
    parser_classes = [FileUploadParser, ]
    def post(self, request, filename, *args, **kwargs):
    print(filename)
    print(request.content_type)
    # 获取请求的值,并使用对应的JSONParser进行处理
    print(request.data)
    # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
    print(request.POST)
    print(request.FILES)
    return Response('POST请求,响应内容')
    def put(self, request, *args, **kwargs):
    return Response('PUT请求,响应内容')

# upload.html --------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">
    <input type="submit" value="提交">
</form>
</body>
</html>

5、同时多个Parser

当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
class TestView(APIView):
    parser_classes = [JSONParser, FormParser, MultiPartParser, ]
    def post(self, request, *args, **kwargs):
        print(request.content_type)
        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')
        
    def put(self, request, *args, **kwargs):
    	return Response('PUT请求,响应内容')

6、全局使用

# settings.py --------------------------------------------------------------------
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
        'rest_framework.parsers.FormParser'
        'rest_framework.parsers.MultiPartParser'
    ]
}

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
    def post(self, request, *args, **kwargs):
        print(request.content_type)
        # 获取请求的值,并使用对应的JSONParser进行处理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST请求,响应内容')
        
    def put(self, request, *args, **kwargs):
        return Response('PUT请求,响应内容')

DRF 序列化

1、自定义字段

(1) 常用字段

字段 语法
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(allow_blank=False, trim_whitespace=True, max_length=None, min_length=None)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, localize=False, rounding=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
EmailField EmailField()
RegexField RegexField(regex)
URLField URLField()
SlugField SlugField(allow_unicode=False)
UUIDField UUIDField(format='hex_verbose') # 可选值('hex_verbose', 'hex', 'int', 'urn')
IPAddressField IPAddressField(protocol='both',unpack_ipv4=False)
DurationField DurationField(max_value=None, min_value=None)
ChoiceField ChoiceField(choices=choices)
MultipleChoiceField MultipleChoiceField(choices=choices)
ListField ListField(child=child, allow_empty=True, max_length=None, min_length=None)
DictField DictField(child=child, allow_empty=True)
FileField FileField()
ImageField ImageField()

(2) 常用属性

属性 说明
max_length 最大长度
min_length 最小长度
max_value 最大值
min_value 最小值
allow_blank 是否允许为空
trim_whitespace 是否去除两边空格
format 字符串格式化一般用户时间日期字段
source 来源,字段名称

(3) 通用参数

属性 说明
error_messages 该包含错误编号于错误信息的字典
read_only 表示该字段仅用于序列化输出,默认False
write_only 表示该字段仅用于反序列化输入,默认False
required 表示该字段仅用于反序列化时必须输入,默认True
default 反序列化输入时使用的默认值
allow_null 表明该字段是否允许传入None, 默认False
allow_blank 表明该字段是否可以不传, 默认False
validators 该字段使用的验证器
label 用户HTML展示API页面时,显示的字段名称
help_text 用户HTML展示API页面时,显示的字段帮助提示信息

(4) 基本使用

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

#views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models
class PasswordValidator(object):
    def __init__(self, base):
    	self.base = base
    def __call__(self, value):
    	if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)
    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass

class UserSerializer(serializers.Serializer):
    ut_title = serializers.CharField(source='ut.title')
    user = serializers.CharField(min_length=6)
    pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])

class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # 序列化,将数据库查询字段序列化为字典
        data_list = models.UserInfo.objects.all()
        ser = UserSerializer(instance=data_list, many=True)
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)
        
    def post(self, request, *args, **kwargs):
        # 验证,对请求发来的数据进行验证
        ser = UserSerializer(data=request.data)
        if ser.is_valid():
        	print(ser.validated_data)
        else:
            print(ser.errors)
            return Response('POST请求,响应内容')

2、基于Model自动生成字段

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
	url(r'test/', TestView.as_view(), name='test'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models
class PasswordValidator(object):
    def __init__(self, base):
    	self.base = str(base)

    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)
            
    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass

class ModelUserSerializer(serializers.ModelSerializer):
	user = serializers.CharField(max_length=32)
    class Meta:
    model = models.UserInfo
    fields = "__all__"
    # fields = ['user', 'pwd', 'ut']
    depth = 2
    extra_kwargs = {'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}}
    # read_only_fields = ['user']

class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # 序列化,将数据库查询字段序列化为字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True)
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)
    
    def post(self, request, *args, **kwargs):
        # 验证,对请求发来的数据进行验证
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
        	print(ser.validated_data)
        else:
            print(ser.errors)
            return Response('POST请求,响应内容')

3、生成URL

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='detail'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models
class PasswordValidator(object):
    def __init__(self, base):
    	self.base = str(base)
    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)
            
    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass

class ModelUserSerializer(serializers.ModelSerializer):
    ut = serializers.HyperlinkedIdentityField(view_name='detail')
    class Meta:
        model = models.UserInfo
        fields = "__all__"
        extra_kwargs = {
            'user': {'min_length': 6},
            'pwd': {'validators': [PasswordValidator(666),]},
        }
    
class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # 序列化,将数据库查询字段序列化为字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)
        
    def post(self, request, *args, **kwargs):
        # 验证,对请求发来的数据进行验证
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
        	print(ser.validated_data)
        else:
            print(ser.errors)
            return Response('POST请求,响应内容')

4、自动生成URL

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='xxxx'),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models
class PasswordValidator(object):
    def __init__(self, base):
    	self.base = str(base)

    def __call__(self, value):
    	if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)
            
    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass

class ModelUserSerializer(serializers.HyperlinkedModelSerializer):
    ll = serializers.HyperlinkedIdentityField(view_name='xxxx')
    tt = serializers.CharField(required=False)
    class Meta:
        model = models.UserInfo
        fields = "__all__"
        list_serializer_class = serializers.ListSerializer
        extra_kwargs = {
            'user': {'min_length': 6},
            'pwd': {'validators': [PasswordValidator(666), ]},
            'url': {'view_name': 'xxxx'},
            'ut': {'view_name': 'xxxx'},
        }
        
class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # # 序列化,将数据库查询字段序列化为字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
        # # 如果Many=True
        # # 或
        # # obj = models.UserInfo.objects.all().first()
        # # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)
        
    def post(self, request, *args, **kwargs):
        # 验证,对请求发来的数据进行验证
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
        	print(ser.validated_data)
        else:
            print(ser.errors)
            return Response('POST请求,响应内容')

DRF 分页

1、根据页码进行分页

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from rest_framework import routers
from web.views import s9_pagination

urlpatterns = [
	url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models
from rest_framework.pagination import PageNumberPagination

class StandardResultsSetPagination(PageNumberPagination):
    # 默认每页显示的数据条数
    page_size = 1
    # 获取URL参数中设置的每页显示数据条数
    page_size_query_param = 'page_size'
    # 获取URL参数中传入的页码key
    page_query_param = 'page'
    # 最大支持的每页显示的数据条数
    max_page_size = 1

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')
        # 实例化分页对象,获取数据库中的分页数据
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
        # 序列化对象
        serializer = UserSerializer(page_user_list, many=True)
        # 生成分页和数据
        response = paginator.get_paginated_response(serializer.data)
        return response

2、位置和个数进行分页

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s9_pagination
urlpatterns = [
	url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination

class StandardResultsSetPagination(LimitOffsetPagination):
    # 默认每页显示的数据条数
    default_limit = 10
    # URL中传入的显示数据条数的参数
    limit_query_param = 'limit'
    # URL中传入的数据位置的参数
    offset_query_param = 'offset'
    # 最大每页显得条数
    max_limit = None

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')
        # 实例化分页对象,获取数据库中的分页数据
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
        # 序列化对象
        serializer = UserSerializer(page_user_list, many=True)
        # 生成分页和数据
        response = paginator.get_paginated_response(serializer.data)
        return response

3、游标分页

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s9_pagination
urlpatterns = [
	url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class StandardResultsSetPagination(CursorPagination):
    # URL传入的游标参数
    cursor_query_param = 'cursor'
    # 默认每页显示的数据条数
    page_size = 2
    # URL传入的每页显示条数的参数
    page_size_query_param = 'page_size'
    # 每页显示数据最大条数
    max_page_size = 1000
    # 根据ID从大到小排列
    ordering = "id"

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')
        # 实例化分页对象,获取数据库中的分页数据
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
        # 序列化对象
        serializer = UserSerializer(page_user_list, many=True)
        # 生成分页和数据
        response = paginator.get_paginated_response(serializer.data)
        return response

DRF 路由系统

1、自定义路由

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view())
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from .. import models
class TestView(APIView):
    def get(self, request, *args, **kwargs):
        print(kwargs)
        print(self.renderer_classes)
        return Response('...')

2、半自动路由

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s10_generic
urlpatterns = [
    url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
    {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

# views.py --------------------------------------------------------------------
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

3、全自动路由

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from rest_framework import routers
from web.views import s10_generic
router = routers.DefaultRouter()
router.register(r'users', s10_generic.UserViewSet)
urlpatterns = [
	url(r'^', include(router.urls)),
]

# views.py --------------------------------------------------------------------
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

DRF 视图

1、GenericViewSet

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views.s7_viewset import TestView
urlpatterns = [
    url(r'test/', TestView.as_view({'get':'list'}), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view({'get':'list'}), name='xxxx'),
]

# views.py --------------------------------------------------------------------
from rest_framework import viewsets
from rest_framework.response import Response
class TestView(viewsets.GenericViewSet):
    def list(self, request, *args, **kwargs):
    	return Response('...')
    def add(self, request, *args, **kwargs):
    	pass
    def delete(self, request, *args, **kwargs):
    	pass
    def edit(self, request, *args, **kwargs):
    	pass

2、ModelViewSet(自定义URL)

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s10_generic
urlpatterns = [
    url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
    {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

views.py:
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

3、ModelViewSet(rest framework路由)

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from rest_framework import routers
from app01 import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
	url(r'^', include(router.urls)),
]

# views.py --------------------------------------------------------------------
from rest_framework import viewsets
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
    	model = models.User
    	fields = ('url', 'username', 'email', 'groups')

class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Group
        fields = ('url', 'name')

class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

DRF 渲染器

1、简介

根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。
用户请求URL:
    http://127.0.0.1:8000/test/?format=json
    http://127.0.0.1:8000/test.json
用户请求头:
	Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

2、json

"""
访问URL:
    http://127.0.0.1:8000/test/?format=json
    http://127.0.0.1:8000/test.json
    http://127.0.0.1:8000/test/ 
"""

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from .. import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class TestView(APIView):
    renderer_classes = [JSONRenderer, ]
    def get(self, request, *args, **kwargs):
    user_list = models.UserInfo.objects.all()
    ser = TestSerializer(instance=user_list, many=True)
    return Response(ser.data)

3、表格

"""
访问URL:
    http://127.0.0.1:8000/test/?format=admin
    http://127.0.0.1:8000/test.admin
    http://127.0.0.1:8000/test/ 
"""

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import AdminRenderer
from .. import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class TestView(APIView):
    renderer_classes = [AdminRenderer, ]
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all()
        ser = TestSerializer(instance=user_list, many=True)
        return Response(ser.data)

4、Form表单

"""
访问URL:
    http://127.0.0.1:8000/test/?format=form
    http://127.0.0.1:8000/test.form
    http://127.0.0.1:8000/test/ 
"""

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import AdminRenderer
from rest_framework.renderers import HTMLFormRenderer
from .. import models
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class TestView(APIView):
    renderer_classes = [HTMLFormRenderer, ]
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data)

5、自定义显示模板

"""
访问URL:
    http://127.0.0.1:8000/test/?format=html
    http://127.0.0.1:8000/test.html
    http://127.0.0.1:8000/test/
"""

# urls.py --------------------------------------------------------------------
from django.conf.urls import url, include
from web.views import s11_render
urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRenderer
from .. import models

class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class TestView(APIView):
    renderer_classes = [TemplateHTMLRenderer, ]
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data, template_name='user_detail.html')

# userdetail.html --------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ user }}
    {{ pwd }}
    {{ ut }}
</body>
</html>

6、浏览器格式API+JSON

"""
访问URL:
    http://127.0.0.1:8000/test/?format=api
    http://127.0.0.1:8000/test.api
    http://127.0.0.1:8000/test/ 
"""

# views.py --------------------------------------------------------------------
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import BrowsableAPIRenderer
from .. import models

class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
    	return JSONRenderer()

class TestView(APIView):
    renderer_classes = [CustomBrowsableAPIRenderer, ]
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data, template_name='user_detail.html')

注意:如果同时多个存在时,自动根据URL后缀来选择渲染器

DRF 过滤

安装:

pip install django-filter

过滤器说明

过滤器 描述
AllValuesFilter 所有值滤波器
AllValuesMultipleFilter 全值多重滤波器
BaseCSVFilter 基本CSV滤波器
BaseInFilter 过滤基
BaseRangeFilter 基地范围内过滤器
BooleanFilter 布尔过滤器
CharFilter 字符过滤器
ChoiceFilter 选择过滤器
DateFilter 日期过滤器
DateFromToRangeFilter 日期范围过滤器
DateRangeFilter 日期范围过滤器
DateTimeFilter 日期时间滤波器
DateTimeFromToRangeFilter 日期时间从范围过滤器
DurationFilter 持续时间过滤
Filter 过滤器
IsoDateTimeFilter Iso日期时间过滤器
IsoDateTimeFromToRangeFilter Iso日期时间范围过滤器
LookupChoiceFilter 查找选择过滤器
ModelChoiceFilter 模型选择过滤器
ModelMultipleChoiceFilter 模型选择滤波器
MultipleChoiceFilter 多项选择过滤器
NumberFilter 数字滤波器
NumericRangeFilter 数字范围过滤器
OrderingFilter 排序滤波器
RangeFilter 范围过滤器
TimeFilter 时间滤波器
TimeRangeFilter 时间范围过滤器
TypedChoiceFilter 类型选择过滤器
TypedMultipleChoiceFilter 类型选择滤波器
UUIDFilter UUID过滤器

自定义过滤器字段对照表

models.AutoField:                   {'filter_class': NumberFilter},
models.CharField:                   {'filter_class': CharFilter},
models.TextField:                   {'filter_class': CharFilter},
models.BooleanField:                {'filter_class': BooleanFilter},
models.DateField:                   {'filter_class': DateFilter},
models.DateTimeField:               {'filter_class': DateTimeFilter},
models.TimeField:                   {'filter_class': TimeFilter},
models.DurationField:               {'filter_class': DurationFilter},
models.DecimalField:                {'filter_class': NumberFilter},
models.SmallIntegerField:           {'filter_class': NumberFilter},
models.IntegerField:                {'filter_class': NumberFilter},
models.PositiveIntegerField:        {'filter_class': NumberFilter},
models.PositiveSmallIntegerField:   {'filter_class': NumberFilter},
models.FloatField:                  {'filter_class': NumberFilter},
models.NullBooleanField:            {'filter_class': BooleanFilter},
models.SlugField:                   {'filter_class': CharFilter},
models.EmailField:                  {'filter_class': CharFilter},
models.FilePathField:               {'filter_class': CharFilter},
models.URLField:                    {'filter_class': CharFilter},
models.GenericIPAddressField:       {'filter_class': CharFilter},
models.CommaSeparatedIntegerField:  {'filter_class': CharFilter},
models.UUIDField:                   {'filter_class': UUIDFilter},

1、自定义过滤

class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    pagination_class = AuthorsCursorPagination
    def get_queryset(self):
    	return Goods.objects.filter(click_num__gt=500)

class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    pagination_class = AuthorsCursorPagination
    def get_queryset(self):
        queryset = Goods.objects.all()
        price_min = self.request.query_params.get("price_min",0)
        if price_min:
        	queryset = queryset.filter(click_num__gt=int(price_min))
        return queryset		

2、DjangoFilterBackend 过滤

精确过滤

# 方式一 ----------------------------------------------------------------------
from rest_framework import mixins
from rest_framework import viewsets
from api.utils.serializer import GoodsSerialize
from api.utils.pager import AuthorsCursorPagination
from .models import Goods
from django_filters.rest_framework import DjangoFilterBackend
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    pagination_class = AuthorsCursorPagination
    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ('name', 'click_num')

# 方式二 ---------------------------------------------------------------------
# filters.py
# 自定义过滤器
from django_filters import rest_framework as filters
from api.models import Goods
class GoodsFilter(filters.FilterSet):
    min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
    max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
    class Meta:
        model = Goods
        fields = ['min_price', 'max_price']
        
# views.py
# DjangoFilterBackend 过滤(精确过滤)
from rest_framework import mixins
from rest_framework import viewsets
from api.utils.serializer import GoodsSerialize
from api.utils.pager import AuthorsCursorPagination
from .models import Goods
from django_filters.rest_framework import DjangoFilterBackend
from api.utils.filters import GoodsFilter
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    pagination_class = AuthorsCursorPagination
    filter_backends = (DjangoFilterBackend,)
    filter_class = GoodsFilter

3、SearchFilter 搜索

# urls.py
url(r'search_list/$',views.SearchList.as_view()),

# views.py
# 2.搜索查询(SearchFilter)
from rest_framework import generics
from rest_framework import filters
from .models import Goods
from api.utils.serializer import GoodsSerialize
# http://127.0.0.1:8000/front-end/api/v1/search_list/?search=草莓
class SearchList(generics.ListAPIView):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    filter_backends = (filters.SearchFilter,)
    search_fields = ('^name',)

4、OrderingFilter 排序查询

# urls.py
url(r'order/$',views.Order.as_view()),
	
# views.py
#排序查询:
# http://127.0.0.1:8000/front-end/api/v1/order/?ordering=shop_price&search=草莓
class Order(generics.ListAPIView):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    filter_backends = (filters.SearchFilter, filters.OrderingFilter,)
    search_fields = ('name',)
    ordering_fields = ('shop_price',)

5、总结(分页,过滤,搜索,排序)


# urls.py
url(r'filter_list/$',views.FilterList.as_view({'get':'list'})),

# views.py
from rest_framework import filters
from rest_framework import mixins
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Goods
from api.utils.filters import GoodsFilter
from api.utils.serializer import GoodsSerialize
from api.utils.pager import AuthorsCursorPagination
class FilterList(mixins.ListModelMixin,viewsets.GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerialize
    pagination_class = AuthorsCursorPagination
    filter_backends = (DjangoFilterBackend,filters.SearchFilter, filters.OrderingFilter,)
    filterset_class = GoodsFilter
    search_fields = ('name',)
    ordering_fields = ('shop_price',)
posted @ 2023-05-31 09:32  菜鸟程序员_python  阅读(62)  评论(0)    收藏  举报