DjangoRestFrameWork
安装:pip install djangorestframework
restful规范
-
API与用户的通信协议,总是使用HTTPs协议(我们现在用的大部分都是http)
-
域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单(推荐使用)
-
版本
- URL,如:https://api.example.com/v1/
- 请求头跨域时,引发发送多次请求(解决:https://www.cnblogs.com/xingxingnbsp/p/10644135.html)
-
路径,视网络上任何东西都是资源,均使用名词表示(可复数)
-
method
- GET :从服务器取出资源(一项或多项)
- POST :在服务器新建一个资源
- PUT :在服务器更新资源(客户端提供改变后的完整资源)
- PATCH :在服务器更新资源(客户端提供改变的属性)
- DELETE :从服务器删除资源
-
过滤,通过在url上传参的形式传递搜索条件
- https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
- https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
-
状态码
- 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - []:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
- 401 Unauthorized - []:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - []:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR - []:服务器发生错误,用户将无法判断发出的请求是否成功。
- 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
-
错误处理,状态码是4xx时,应返回错误信息,error当做key。
{ error: "Invalid API key" } -
返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
- GET /collection:返回资源对象的列表(数组)
- GET /collection/resource:返回单个资源对象
- POST /collection:返回新生成的资源对象
- PUT /collection/resource:返回完整的资源对象
- PATCH /collection/resource:返回完整的资源对象
- DELETE /collection/resource:返回一个空文档
-
Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{ "link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" } }
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、总结
使用:
- 类,继承 BasePermission
- 必须实现 has_permission 方法
- 返回值 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',)
浙公网安备 33010602011771号