RESTful API设计

  • API与用户的通信协议,总是使用HTTPs协议。
  • 域名 
    • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
    • https://example.org/api/                        API很简单
  • 版本
    • URL,如:https://api.example.com/v1/
    • 请求头                                                  跨域时,引发发送多次请求
  • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
    • https://api.example.com/v1/zoos
    • https://api.example.com/v1/animals
    • https://api.example.com/v1/employees
  • 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"
    }}

  摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

三. 基于Django实现

路由系统:

urlpatterns = [url(r'^users', Users.as_view()),
]

CBV视图:

from django.views import View
from django.http import JsonResponseclass Users(View):def get(self, request, *args, **kwargs):result = {'status': True,'data': 'response data'}return JsonResponse(result, status=200)def post(self, request, *args, **kwargs):result = {'status': True,'data': 'response data'}return JsonResponse(result, status=200) 

四. 基于Django Rest Framework框架实现

1. 基本流程

url.py

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

views.py

from rest_framework.views import APIView
from rest_framework.response import Responseclass TestView(APIView):def dispatch(self, request, *args, **kwargs):"""请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法注意:APIView中的dispatch方法有好多好多的功能"""return super().dispatch(request, *args, **kwargs)def get(self, request, *args, **kwargs):return Response('GET请求,响应内容')def post(self, request, *args, **kwargs):return Response('POST请求,响应内容')def put(self, request, *args, **kwargs):return Response('PUT请求,响应内容')

上述是rest framework框架基本流程,重要的功能是在APIView的dispatch中触发。

2.  认证和授权

a. 用户url传入的token认证

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

urls.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 exceptionstoken_list = ['sfsfss123kuf3j123','asijnfowerkkf9812',
]class TestAuthentication(BaseAuthentication):def authenticate(self, request):"""用户认证,如果验证成功后返回元组: (用户,用户Token):param request: :return: None,表示跳过该验证;如果跳过了所有认证,默认用户和Token和使用配置文件进行设置self._authenticator = Noneif api_settings.UNAUTHENTICATED_USER:self.user = api_settings.UNAUTHENTICATED_USER()else:self.user = Noneif api_settings.UNAUTHENTICATED_TOKEN:self.auth = api_settings.UNAUTHENTICATED_TOKEN()else:self.auth = None(user,token)表示验证通过并设置用户名和Token;AuthenticationFailed异常"""val = request.query_params.get('token')if val not in token_list:raise exceptions.AuthenticationFailed("用户认证失败")return ('登录用户', '用户token')def authenticate_header(self, request):"""Return a string to be used as the value of the `WWW-Authenticate`header in a `401 Unauthenticated` response, or `None` if theauthentication scheme should return `403 Permission Denied` responses."""# 验证失败时,返回的响应头WWW-Authenticate对应的值passclass 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请求,响应内容')

views.py

b. 请求头认证

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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 exceptionstoken_list = ['sfsfss123kuf3j123','asijnfowerkkf9812',
]class TestAuthentication(BaseAuthentication):def authenticate(self, request):"""用户认证,如果验证成功后返回元组: (用户,用户Token):param request: :return: None,表示跳过该验证;如果跳过了所有认证,默认用户和Token和使用配置文件进行设置self._authenticator = Noneif api_settings.UNAUTHENTICATED_USER:self.user = api_settings.UNAUTHENTICATED_USER()else:self.user = Noneif api_settings.UNAUTHENTICATED_TOKEN:self.auth = api_settings.UNAUTHENTICATED_TOKEN()else:self.auth = None(user,token)表示验证通过并设置用户名和Token;AuthenticationFailed异常"""import base64auth = 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('验证失败')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):"""Return a string to be used as the value of the `WWW-Authenticate`header in a `401 Unauthenticated` response, or `None` if theauthentication scheme should return `403 Permission Denied` responses."""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请求,响应内容')

views.py

c. 多个认证规则

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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 exceptionstoken_list = ['sfsfss123kuf3j123','asijnfowerkkf9812',
]class Test1Authentication(BaseAuthentication):def authenticate(self, request):"""用户认证,如果验证成功后返回元组: (用户,用户Token):param request: :return: None,表示跳过该验证;如果跳过了所有认证,默认用户和Token和使用配置文件进行设置self._authenticator = Noneif api_settings.UNAUTHENTICATED_USER:self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户else:self.user = Noneif api_settings.UNAUTHENTICATED_TOKEN:self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:Noneelse:self.auth = None(user,token)表示验证通过并设置用户名和Token;AuthenticationFailed异常"""import base64auth = request.META.get('HTTP_AUTHORIZATION', b'')if auth:auth = auth.encode('utf-8')else:return Noneprint(auth,'xxxx')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):"""Return a string to be used as the value of the `WWW-Authenticate`header in a `401 Unauthenticated` response, or `None` if theauthentication scheme should return `403 Permission Denied` responses."""# return 'Basic realm=api'passclass Test2Authentication(BaseAuthentication):def authenticate(self, request):"""用户认证,如果验证成功后返回元组: (用户,用户Token):param request: :return: None,表示跳过该验证;如果跳过了所有认证,默认用户和Token和使用配置文件进行设置self._authenticator = Noneif api_settings.UNAUTHENTICATED_USER:self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户else:self.user = Noneif api_settings.UNAUTHENTICATED_TOKEN:self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:Noneelse:self.auth = None(user,token)表示验证通过并设置用户名和Token;AuthenticationFailed异常"""val = request.query_params.get('token')if val not in token_list:raise exceptions.AuthenticationFailed("用户认证失败")return ('登录用户', '用户token')def authenticate_header(self, request):"""Return a string to be used as the value of the `WWW-Authenticate`header in a `401 Unauthenticated` response, or `None` if theauthentication scheme should return `403 Permission Denied` responses."""passclass 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请求,响应内容')

views.py

d. 认证和权限

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermissionfrom rest_framework.request import Request
from rest_framework import exceptionstoken_list = ['sfsfss123kuf3j123','asijnfowerkkf9812',
]class TestAuthentication(BaseAuthentication):def authenticate(self, request):"""用户认证,如果验证成功后返回元组: (用户,用户Token):param request: :return: None,表示跳过该验证;如果跳过了所有认证,默认用户和Token和使用配置文件进行设置self._authenticator = Noneif api_settings.UNAUTHENTICATED_USER:self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户else:self.user = Noneif api_settings.UNAUTHENTICATED_TOKEN:self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:Noneelse:self.auth = None(user,token)表示验证通过并设置用户名和Token;AuthenticationFailed异常"""val = request.query_params.get('token')if val not in token_list:raise exceptions.AuthenticationFailed("用户认证失败")return ('登录用户', '用户token')def authenticate_header(self, request):"""Return a string to be used as the value of the `WWW-Authenticate`header in a `401 Unauthenticated` response, or `None` if theauthentication scheme should return `403 Permission Denied` responses."""passclass 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 Trueclass TestView(APIView):# 认证的动作是由request.user触发authentication_classes = [TestAuthentication, ]# 权限# 循环执行所有的权限permission_classes = [TestPermission, ]def get(self, request, *args, **kwargs):# self.dispatchprint(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请求,响应内容')

views.py

e. 全局使用

上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。

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

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Responseclass TestView(APIView):def get(self, request, *args, **kwargs):# self.dispatchprint(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请求,响应内容')

views.py

3. 用户访问次数/频率限制

a. 基于用户IP限制访问频率

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
from rest_framework.views import APIView
from rest_framework.response import Responsefrom 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.timedef get_ident(self, request):"""根据用户IP和代理IP,当做请求者的唯一IPIdentify the machine making the request by parsing HTTP_X_FORWARDED_FORif present and number of proxies is > 0. If not use all ofHTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR."""xff = request.META.get('HTTP_X_FORWARDED_FOR')remote_addr = request.META.get('REMOTE_ADDR')num_proxies = api_settings.NUM_PROXIESif num_proxies is not None:if num_proxies == 0 or xff is None:return remote_addraddrs = xff.split(',')client_addr = addrs[-min(num_proxies, len(addrs))]return client_addr.strip()return ''.join(xff.split()) if xff else remote_addrdef 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 = 10time_request = 60now = self.ctime()ident = self.get_ident(request)self.ident = identif ident not in RECORD:RECORD[ident] = [now, ]return Truehistory = RECORD[ident]while history and history[-1] <= now - time_request:history.pop()if len(history) < num_request:history.insert(0, now)return Truedef wait(self):"""多少秒后可以允许继续访问Optionally, return a recommended number of seconds to wait beforethe 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.dispatchprint(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)

views.py

b. 基于用户IP显示访问频率(利于Django缓存)

REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'test_scope': '10/m',},
}

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Responsefrom rest_framework import exceptions
from rest_framework.throttling import SimpleRateThrottleclass TestThrottle(SimpleRateThrottle):# 配置文件定义的显示频率的Keyscope = "test_scope"def get_cache_key(self, request, view):"""Should return a unique cache-key which can be used for throttling.Must be overridden.May return `None` if the request should not be throttled."""if not request.user:ident = self.get_ident(request)else:ident = request.userreturn self.cache_format % {'scope': self.scope,'ident': ident}class TestView(APIView):throttle_classes = [TestThrottle, ]def get(self, request, *args, **kwargs):# self.dispatchprint(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)

views.py

c. view中限制请求频率

REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'xxxxxx': '10/m',},
}

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Responsefrom rest_framework import exceptions
from rest_framework.throttling import ScopedRateThrottle# 继承 ScopedRateThrottle
class TestThrottle(ScopedRateThrottle):def get_cache_key(self, request, view):"""Should return a unique cache-key which can be used for throttling.Must be overridden.May return `None` if the request should not be throttled."""if not request.user:ident = self.get_ident(request)else:ident = request.userreturn 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.dispatchprint(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)

views.py

d. 匿名时用IP限制+登录时用Token限制

REST_FRAMEWORK = {'UNAUTHENTICATED_USER': None,'UNAUTHENTICATED_TOKEN': None,'DEFAULT_THROTTLE_RATES': {'luffy_anon': '10/m','luffy_user': '20/m',},
}

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Responsefrom rest_framework.throttling import SimpleRateThrottleclass LuffyAnonRateThrottle(SimpleRateThrottle):"""匿名用户,根据IP进行限制"""scope = "luffy_anon"def get_cache_key(self, request, view):# 用户已登录,则跳过 匿名频率限制if request.user:return Nonereturn 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.tokenreturn "user_token"def get_cache_key(self, request, view):"""获取缓存key:param request: :param view: :return: """# 未登录用户,则跳过 Token限制if not request.user:return Nonereturn 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.dispatchprint(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请求,响应内容')

views.py

e. 全局使用

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',},
}

settings

4. 版本

a. 基于url的get传参方式

如:/users?version=v1

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

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioningclass TestView(APIView):versioning_class = QueryParameterVersioningdef get(self, request, *args, **kwargs):# 获取版本print(request.version)# 获取版本管理的类print(request.versioning_scheme)# 反向生成URLreverse_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请求,响应内容')

views.py

b. 基于url的正则方式

如:/v1/users/

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

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioningclass TestView(APIView):versioning_class = URLPathVersioningdef get(self, request, *args, **kwargs):# 获取版本print(request.version)# 获取版本管理的类print(request.versioning_scheme)# 反向生成URLreverse_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请求,响应内容')

views.py

c. 基于 accept 请求头方式

如:Accept: application/json; version=1.0

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

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioningclass TestView(APIView):versioning_class = AcceptHeaderVersioningdef get(self, request, *args, **kwargs):# 获取版本 HTTP_ACCEPT头print(request.version)# 获取版本管理的类print(request.versioning_scheme)# 反向生成URLreverse_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请求,响应内容')

views.py

d. 基于主机名方法

如:v1.example.com

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

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioningclass TestView(APIView):versioning_class = HostNameVersioningdef get(self, request, *args, **kwargs):# 获取版本print(request.version)# 获取版本管理的类print(request.versioning_scheme)# 反向生成URLreverse_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请求,响应内容')

views.py

e. 基于django路由系统的namespace

如:example.com/v1/users/

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

settings.py

from django.conf.urls import url, include
from web.views import TestViewurlpatterns = [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')),]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioningclass TestView(APIView):versioning_class = NamespaceVersioningdef get(self, request, *args, **kwargs):# 获取版本print(request.version)# 获取版本管理的类print(request.versioning_scheme)# 反向生成URLreverse_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请求,响应内容')

views.py

f. 全局使用

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

settings.py

5. 解析器(parser)

根据请求头 content-type 选择对应的解析器就请求体内容进行处理。

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

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParserclass 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请求,响应内容')

views.py

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

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParserclass 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请求,响应内容')

views.py

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

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParserclass 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请求,响应内容')

views.py

<!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>

upload.html

d. 仅上传文件

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParserclass 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请求,响应内容')

views.py

<!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>

upload.html

e. 同时多个Parser

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

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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, MultiPartParserclass 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请求,响应内容')

views.py

f. 全局使用

REST_FRAMEWORK = {'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser''rest_framework.parsers.FormParser''rest_framework.parsers.MultiPartParser']}

settings.py

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Responseclass 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请求,响应内容')

views.py

注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取

6. 序列化

序列化用于对用户请求数据进行验证和数据进行序列化。

a. 自定义字段

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import modelsclass PasswordValidator(object):def __init__(self, base):self.base = basedef __call__(self, value):if value != self.base:message = 'This field must be %s.' % self.baseraise 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是当前字段对象passclass 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请求,响应内容')

views.py

b. 基于Model自动生成字段

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import modelsclass 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.baseraise 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是当前字段对象passclass ModelUserSerializer(serializers.ModelSerializer):user = serializers.CharField(max_length=32)class Meta:model = models.UserInfofields = "__all__"# fields = ['user', 'pwd', 'ut']depth = 2extra_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请求,响应内容')

views.py

c. 生成URL

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import modelsclass 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.baseraise 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是当前字段对象passclass ModelUserSerializer(serializers.ModelSerializer):ut = serializers.HyperlinkedIdentityField(view_name='detail')class Meta:model = models.UserInfofields = "__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请求,响应内容')

views.py

d. 自动生成URL

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import modelsclass 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.baseraise 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是当前字段对象passclass ModelUserSerializer(serializers.HyperlinkedModelSerializer):ll = serializers.HyperlinkedIdentityField(view_name='xxxx')tt = serializers.CharField(required=False)class Meta:model = models.UserInfofields = "__all__"list_serializer_class = serializers.ListSerializerextra_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请求,响应内容')

views.py

7. 分页

a. 根据页码进行分页

from django.conf.urls import url, include
from rest_framework import routers
from web.views import s9_paginationurlpatterns = [url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urs.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import modelsfrom rest_framework.pagination import PageNumberPaginationclass StandardResultsSetPagination(PageNumberPagination):# 默认每页显示的数据条数page_size = 1# 获取URL参数中设置的每页显示数据条数page_size_query_param = 'page_size'# 获取URL参数中传入的页码keypage_query_param = 'page'# 最大支持的每页显示的数据条数max_page_size = 1class UserSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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

views.py

b. 位置和个数进行分页

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import modelsfrom rest_framework.pagination import PageNumberPagination,LimitOffsetPaginationclass StandardResultsSetPagination(LimitOffsetPagination):# 默认每页显示的数据条数default_limit = 10# URL中传入的显示数据条数的参数limit_query_param = 'limit'# URL中传入的数据位置的参数offset_query_param = 'offset'# 最大每页显得条数max_limit = Noneclass UserSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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

views.py

c. 游标分页

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import modelsfrom rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPaginationclass 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.UserInfofields = "__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

views.py

8. 路由系统

a. 自定义路由

from django.conf.urls import url, include
from web.views import s11_renderurlpatterns = [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())
]

urls.py

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

views.py

b. 半自动路由

from django.conf.urls import url, include
from web.views import s10_genericurlpatterns = [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'})),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import modelsclass UserSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__all__"class UserViewSet(ModelViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserSerializer

views.py

c. 全自动路由

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

urls.py

from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import modelsclass UserSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__all__"class UserViewSet(ModelViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserSerializer

views.py

9. 视图

a. GenericViewSet

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import viewsets
from rest_framework.response import Responseclass TestView(viewsets.GenericViewSet):def list(self, request, *args, **kwargs):return Response('...')def add(self, request, *args, **kwargs):passdef delete(self, request, *args, **kwargs):passdef edit(self, request, *args, **kwargs):pass

views.py

b. ModelViewSet(自定义URL)

from django.conf.urls import url, include
from web.views import s10_genericurlpatterns = [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'})),
]

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import modelsclass UserSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__all__"class UserViewSet(ModelViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserSerializer

views.py

c. ModelViewSet(rest framework路由)

from django.conf.urls import url, include
from rest_framework import routers
from app01 import viewsrouter = 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)),
]

urls.py

from rest_framework import viewsets
from rest_framework import serializersclass UserSerializer(serializers.HyperlinkedModelSerializer):class Meta:model = models.Userfields = ('url', 'username', 'email', 'groups')class GroupSerializer(serializers.HyperlinkedModelSerializer):class Meta:model = models.Groupfields = ('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 = UserSerializerclass GroupViewSet(viewsets.ModelViewSet):"""API endpoint that allows groups to be viewed or edited."""queryset = Group.objects.all()serializer_class = GroupSerializer

views.py

10. 渲染器

根据 用户请求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

a. 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/

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom rest_framework.renderers import JSONRendererfrom .. import modelsclass TestSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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)

views.py

b. 表格

访问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/

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom rest_framework.renderers import AdminRendererfrom .. import modelsclass TestSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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)

views.py

c. 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/

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom rest_framework.renderers import JSONRenderer
from rest_framework.renderers import AdminRenderer
from rest_framework.renderers import HTMLFormRendererfrom .. import modelsclass TestSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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)

views.py

d. 自定义显示模板

访问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/

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

urls.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRendererfrom .. import modelsclass TestSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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')

views.py

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

userdetail.html

e. 浏览器格式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/

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom rest_framework.renderers import JSONRenderer
from rest_framework.renderers import BrowsableAPIRendererfrom .. import modelsclass TestSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__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')

views.py

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

转载于:https://www.cnblogs.com/liuguniang/p/8085944.html

REST framework(2)相关推荐

  1. iOS 制作framework(SDK)的流程

    iOS 制作framework(SDK)的流程 项目需要写SDK,在这里整理一下方法和思路. 第一步:创建framwwork 第二步:随便取个名字:Hello,然后点击Next,指定一个你要存的位置 ...

  2. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(一)

    文章大纲 引言 一.Android Storage Access Framework 二.Storage Access Framework 的主要角色成员 1.Document Provider 文件 ...

  3. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)

    文章大纲 引言 一.DirectFragment 1.当选中DirectoryFragment中RecyclerView的Item时 2.选中DirectoryFragment中RecyclerVie ...

  4. 谈谈Django REST Framework(DRF)中的序列化器

    摘要 Django REST Framework(DRF)是一个强大的工具,可以帮助我们构建和处理RESTful API.其中的序列化器(Serializers)是其核心组件之一,它允许我们快速有效地 ...

  5. stripes framework(框架)入门教程(附案例分析)

    stripes框架是基于MVC设计模式的开源的web应用框架,坚持"convention over configuration(惯例优先原则)"致力于比Struts和SpringM ...

  6. Spring Framework(框架)整体架构

    原文链接:https://blog.csdn.net/wd2014610/article/details/80061808 Spring 在这个Spring框架大行其道的软件开发世界里,尚有很多工程师 ...

  7. Django Rest Framework(一)

    一.什么是RESTful REST与技术无关,代表一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为"表征状态转移". R ...

  8. Entity Framework(二)

    1. ORM :Object Relation Mapping ,通俗说:用操作对象的方式来操作数据库. 2. 插入数据不再是执行Insert,而是类似于 Person p=new Person() ...

  9. Metasploit Framework(6)客户端渗透(上)

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 前五篇文章讲解了Metasploit Framewor ...

最新文章

  1. MySQL数据库多种安装方法及企业级安装实践
  2. Windbg Extension NetExt 使用指南 【2】 ---- NetExt 的基本命令介绍
  3. CRM呼叫中心toolbar的cancel按钮会导致session清理
  4. 前端 ---jQuery的补充
  5. poj 1679 TheUniqueMST 最小生成树Kruskal(、Prim待做
  6. 零基础入门专利代理考试需要了解的,持续更新ing
  7. 将网页以页面仅html,不用JS,教你只用纯HTML做出几个实用网页效果
  8. 配置nginx作为下载站点
  9. Linux中 查看mysql配置文件位置
  10. Python制作PPT周报
  11. 4针串口线接法图_RS232串口线接口及接法(串口通信RS232的基本接法)
  12. css 去掉a标签下划线,CSS 解决 a标签去掉下划线 text-decoration: none无效 的解决方案...
  13. 【高级篇 / ZTNA】(7.0) ❀ 01. FortiClient EMS 下载与安装 ❀ FortiGate 防火墙
  14. 自然语言处理(一)——中英文分词
  15. 隐马尔可夫模型HMM学习笔记
  16. Mqtt ----心跳机制 长链接 ping
  17. linux kobject-uevent(热插拔事件)
  18. java中最基本的数据类型,java中的基本数据类型
  19. torch.mannul_seed()使用和踩坑总结
  20. sql server 2008 r2 直接下载地址,可用迅雷下载

热门文章

  1. 整理sqlserver 级联更新和删除 c#调用存储过程返回值
  2. 近期两篇双目图像超分辨算法论文解读 |AAAI2020 SPL2020
  3. 谷歌CEO支持欧盟禁止人脸识别!微软总裁:能用“手术刀”就别用“切肉刀”...
  4. 包云岗:伯克利科研模式的启发
  5. NVIDIA ECCV18论文:超像素采样网络助力语义分割与光流估计(代码将开源)
  6. Python基础知识:当字典碰到类时会发生什么?
  7. OpenCV的resize方法与双线性插值
  8. 深度学习(七十四)半监督Mean teachers
  9. python社招面试_百度大牛总结十条Python面试题陷阱,看看你是否会中招
  10. 更新计算机上的windows模块安装程序_Win10中用DOS命令也可以完成windows更新,这个技巧还真不知道...