美文网首页Python/GoPython
什么是RESTful API以及Django RestFrame

什么是RESTful API以及Django RestFrame

作者: shu_ke | 来源:发表于2018-06-05 22:58 被阅读688次

    一. 什么是RESTful API以及Django RestFramework

    1. 协议

    API与用户的通信协议,总是使用HTTPS协议。

    2. 域名

    3. 版本

    应该将API的版本号放入URL: https://api.example.com/v1/
    另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。GitHub Developer Guide采用这种做法,跨域时会引发多次请求

    4. 路径(Endpoint)

    路径又称"终点"(endpoint),表示API的具体网址。
    在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
    举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

    https://api.example.com/v1/zoos
    https://api.example.com/v1/animals
    https://api.example.com/v1/employees
    

    5. HTTP动词

    对于资源的具体操作类型,由HTTP动词表示。
    常用的HTTP动词有下面五个(括号里是对应的SQL命令)

    GET(SELECT):从服务器取出资源(一项或多项)
    POST(CREATE):在服务器新建一个资源
    PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
    PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)
    DELETE(DELETE):从服务器删除资源
    

    还有两个不常用的HTTP动词

    HEAD:获取资源的元数据
    OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的
    

    下面是一些栗子:

    GET /zoos:列出所有动物园
    POST /zoos:新建一个动物园
    GET /zoos/ID:获取某个指定动物园的信息
    PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
    PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
    DELETE /zoos/ID:删除某个动物园
    GET /zoos/ID/animals:列出某个指定动物园的所有动物
    DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
    

    6. 过滤信息(Filtering)

    如果记录数量很多,服务器不可能都将它们返回给用户.API应该提供参数,过滤返回结果
    常见的参数形式如下:

    ?limit=10:指定返回记录的数量
    ?offset=10:指定返回记录的开始位置。
    ?page=2&per_page=100:指定第几页,以及每页的记录数。
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    ?animal_type_id=1:指定筛选条件
    

    参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

    7. 状态码(Status Codes)

    服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)

    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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
    

    注意: 状态码的完全列表参见这里

    8. 错误处理(Error handling)

    如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

    {
        error: "Invalid API key"
    }
    

    9. 返回结果

    针对不同操作,服务器向用户返回的结果应该符合以下规范

    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
    

    10. Hypermedia API

    RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档:

    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}
    

    上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
    Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

    {
      "current_user_url": "https://api.github.com/user",
      "authorizations_url": "https://api.github.com/authorizations",
      // ...
    }
    

    从上面可以看到,如果想获取当前用户的信息,应该去访问\api.github.com/user,然后就得到了下面结果:

    {
      "message": "Requires authentication",
      "documentation_url": "https://developer.github.com/v3"
    }
    

    上面代码表示,服务器给出了提示信息,以及文档的网址

    11. 其他

    • API的身份认证应该使用OAuth 2.0框架。
    • 服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

    RESTful API 设计指南 - 阮一峰的网络日志
    GitHub - aisuhua/restful-api-design-references: RESTful API 设计参考文献列表

    二. 基于Django的实现

    2.1 路由系统

    from app01 import views
    urlpatterns = [
        url(r'^users', views.UserView.as_view())
    ]
    

    2.2 CBV试图

    class UserView(View):
        def get(self, request, *args, **kwargs):
            result = {"status": True, "data": "This is a get request", "msg": None, "code": 2000}
            return JsonResponse(result, status=200)
    
        def post(self, request, *args, **kwargs):
            result = {"status": True, "data": "This is a post request", "msg": None, "code": 2000}
            return JsonResponse(result, status=200)
    

    三. 基于Django Rest Framework 框架实现

    3.1 基本流程

    路由:URL.py

    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^index', views.IndexView.as_view())
    ]
    

    试图: views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class IndexView(APIView):
        """
        请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发反射 get/post/put 等方法
        注意:APIView中的dispatch方法有好多好多的功能
        """
    
        def dispatch(self, request, *args, **kwargs):
            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请求,响应内容')
    

    ⚠️ : 以上是Django rest framework框架基本流程,重要的功能是在APIView的dispatch中触发,也是框架的源码入口

    3.2 认证和授权

    3.2.1 基于token的验证

    urls.py
    
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>\w+)/', include('app01.urls')),
    ]
    
    $ cat app01/urls.py 
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^auth/$', views.AuthView.as_view()),
        url(r'^user/$', views.UserView.as_view()),
    ]
    
    $ cat models.py 
    from django.db import models
    
    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
    
    
    class UserToken(models.Model):
        user = models.OneToOneField('UserInfo', on_delete=True)
        token = models.CharField(max_length=64)
    
    $ cat views.py
    from django.shortcuts import render, HttpResponse
    
    # Create your views here.
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from app01 import models
    import uuid
    
    class AuthView(APIView):
        authentication_classes = []    # 登录认证接口覆盖默认的toekn认证类
    
        def post(self, request, *args, **kwargs):
            response = {'code': 1000}
            user = request.data.get('username')
            pwd = request.data.get('password')
    
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                response['code'] = 1001
                response['msg'] = '用户或密码错误'
                return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
            try:
                token = str(uuid.uuid4())
                models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                response['token'] = token
            except Exception as e:
                print("Error: ", e)
            return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
    
    class UserView(APIView):
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return HttpResponse('user.get: %s' % request.user)
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('user.post')
    
    $ cat app01/utils/auth.py
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from app01 import models
    
    class TokenAuthtication(BaseAuthentication):
        def authenticate(self, request):
            """
            :param request:
            :return:
            (user,auth) 表示认证成功,并将元组分别赋值给request.user/request.auth
            :raise AuthenticationFailed('认证失败')  表示认证失败
            """
    
            token = request.query_params.get('token')
            if not token:
                raise AuthenticationFailed("用户Token未携带")
    
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise AuthenticationFailed("Token已失效或错误")
            return (token_obj.user.username, token_obj)
    
    $ cat settings.py
    
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.TokenAuthtication', ],
    }
    

    验证:

    1. 首先在数据库中构造请求登录的用户名密码
    2. 构造数据获取token信息
    $ curl -X POST \
    >   http://127.0.0.1:8001/api/v1/auth/ \
    >   -H 'Cache-Control: no-cache' \
    >   -H 'Content-Type: application/json' \
    >   -H 'Postman-Token: 8439ce0a-94ff-3970-c8c2-4c6bc200bb4f' \
    >   -d '{"username": "eric","password":"123"}'
    返回:
    {"code": 1000, "token": "4c90f696-008b-4e30-86fa-4b00b6fc9237"}
    
    1. 携带token访问user接口,获取用户信息
    >   'http://127.0.0.1:8001/api/v1/user/?token=4c90f696-008b-4e30-86fa-4b00b6fc9237' \
    >   -H 'Cache-Control: no-cache' \
    >   -H 'Postman-Token: 1b4125ad-7ad1-1cca-70fd-6a00cca16d96'
    返回:
    user.get: eric
    

    3.2.2 基于请求头认证

    $ cat urls.py
    
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>\w+)/', include('app01.urls')),
    ]
    
    $ cat app01/urls.py 
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^auth/$', views.AuthView.as_view()),
        url(r'^user/$', views.UserView.as_view()),
    ]
    
    $ cat app01/utils/auth.py
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "shuke"
    # Date: 2018/6/1
    
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework import exceptions
    from app01 import models
    
    class HeaderAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request:
            :return:
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
    
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            import uuid
            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('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            try:
                obj = models.UserInfo.objects.filter(username=username, password=password).first()
                if not obj:
                    raise exceptions.AuthenticationFailed('用户名或密码错误')
                token = str(uuid.uuid4())
                token_obj, status = models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                return (token_obj.user.username, token_obj)
            except Exception as e:
                print("Error: ", e)
    
        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 the
            authentication scheme should return `403 Permission Denied` responses.
            """
            return 'Basic realm=api'
    
    $ cat views.py
    from django.shortcuts import render, HttpResponse
    
    # Create your views here.
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from app01.utils.auth import HeaderAuthentication
    from app01 import models
    import uuid
    
    
    class AuthView(APIView):
        authentication_classes = []
    
        def post(self, request, *args, **kwargs):
            response = {'code': 1000}
            user = request.data.get('username')
            pwd = request.data.get('password')
    
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                response['code'] = 1001
                response['msg'] = '用户或密码错误'
                return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
            try:
                token = str(uuid.uuid4())
                models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                response['token'] = token
            except Exception as e:
                print("Error: ", e)
            return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
    
    class UserView(APIView):
        authentication_classes = [HeaderAuthentication, ]
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return HttpResponse('user.get: %s,token: %s' % (request.user, request.auth))
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('user.post')
    

    验证:

    1. 首先在数据库中构造请求登录的用户名密码
    2. 构造数据获取token信息,建议使用postman进行构造测试
    $ curl -X GET \
      http://127.0.0.1:8001/api/v1/user/ \
      -H 'Authorization: Basic c2h1a2U6MTIz' \
      -H 'Cache-Control: no-cache' \
      -H 'Postman-Token: ba3f99ce-da6a-7e5f-3e0b-3c79129ecca1'
    返回:
    user.get: shuke,token: 2d3af34a-1598-4d5a-af56-7f29a706a26e
    

    3.2.3 多个认证规则

    $ cat urls.py
    
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>\w+)/', include('app01.urls')),
    ]
    
    $ cat app01/urls.py 
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^auth/$', views.AuthView.as_view()),
        url(r'^user/$', views.UserView.as_view()),
    ]
    
    $ cat app01/utils/auth.py
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "shuke"
    # Date: 2018/6/1
    
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework import exceptions
    from app01 import models
    
    
    class TokenAuthtication(BaseAuthentication):
        def authenticate(self, request):
            """
    
            :param request:
            :return:
            (user,auth) 表示认证成功,并将元组分别赋值给request.user/request.auth
            :raise AuthenticationFailed('认证失败')  表示认证失败
            """
    
            token = request.query_params.get('token')
            if not token:
                raise AuthenticationFailed("用户Token未携带")
    
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise AuthenticationFailed("Token已失效或错误")
            return (token_obj.user.username, token_obj)
    
    
    class HeaderAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request:
            :return:
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
    
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            import uuid
            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('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            try:
                obj = models.UserInfo.objects.filter(username=username, password=password).first()
                if not obj:
                    raise exceptions.AuthenticationFailed('用户名或密码错误')
                token = str(uuid.uuid4())
                token_obj, status = models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                return (token_obj.user.username, token_obj)
            except Exception as e:
                print("Error: ", e)
    
        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 the
            authentication scheme should return `403 Permission Denied` responses.
            """
            return 'Basic realm=api'
    
    $ cat views.py
    from django.shortcuts import render, HttpResponse
    
    # Create your views here.
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from app01.utils.auth import HeaderAuthentication, TokenAuthtication
    from app01 import models
    import uuid
    
    
    class AuthView(APIView):
        authentication_classes = []
    
        def post(self, request, *args, **kwargs):
            response = {'code': 1000}
            user = request.data.get('username')
            pwd = request.data.get('password')
    
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                response['code'] = 1001
                response['msg'] = '用户或密码错误'
                return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
            try:
                token = str(uuid.uuid4())
                models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                response['token'] = token
            except Exception as e:
                print("Error: ", e)
            return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
    
    class UserView(APIView):
        authentication_classes = [HeaderAuthentication, TokenAuthtication]
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return HttpResponse('user.get: %s,token: %s' % (request.user, request.auth))
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('user.post')
    

    验证:

    1. 首先在数据库中构造请求登录的用户名密码
    2. 构造数据获取token信息,建议使用postman进行构造测试
    $ curl -X GET \
      http://127.0.0.1:8001/api/v1/user/ \
      -H 'Authorization: Basic c2h1a2U6MTIz' \
      -H 'Cache-Control: no-cache' \
      -H 'Postman-Token: f91805b7-c742-b713-0010-c75cdbfbb24a'
    返回:
    user.get: shuke,token: 9c3ca96a-ac03-4207-aaec-20433bae6058
    

    3.2.4 认证和权限

    路由

    $ cat urls.py
    
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>\w+)/', include('app01.urls')),
    ]
    
    $ cat app01/urls.py 
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^auth/$', views.AuthView.as_view()),
        url(r'^user/$', views.UserView.as_view()),
    ]
    

    model

    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, '普通用户'),
            (2, '管理员'),
            (3, '超级管理员'),
        )
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        user_type = models.IntegerField(choices=user_type_choices, default=1)
    
    
    class UserToken(models.Model):
        user = models.OneToOneField('UserInfo', on_delete=True)
        token = models.CharField(max_length=64)
    

    认证

    $ cat app01/utils/auth.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "shuke"
    # Date: 2018/6/1
    
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from rest_framework import exceptions
    from app01 import models
    
    
    class TokenAuthtication(BaseAuthentication):
        def authenticate(self, request):
            """
    
            :param request:
            :return:
            (user,auth) 表示认证成功,并将元组分别赋值给request.user/request.auth
            :raise AuthenticationFailed('认证失败')  表示认证失败
            """
    
            token = request.query_params.get('token')
            if not token:
                raise AuthenticationFailed("用户Token未携带")
    
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise AuthenticationFailed("Token已失效或错误")
            return (token_obj.user.username, token_obj)
    
    
    class HeaderAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request:
            :return:
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
    
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            import uuid
            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('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            try:
                obj = models.UserInfo.objects.filter(username=username, password=password).first()
                if not obj:
                    raise exceptions.AuthenticationFailed('用户名或密码错误')
                token = str(uuid.uuid4())
                token_obj, status = models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                return (token_obj.user.username, token_obj)
            except Exception as e:
                print("Error: ", e)
                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 the
            authentication scheme should return `403 Permission Denied` responses.
            """
            return 'Basic realm=api'
    

    权限

    $ cat app01/utils/permission.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "shuke"
    # Date: 2018/6/3
    from rest_framework.permissions import BasePermission
    
    
    class UserPermission(BasePermission):
        """
        权限验证
        """
    
        def has_permission(self, request, view):
            """
            判断是否有权限访问当前请求
            Return `True` if permission is granted, `False` otherwise.
            :param request:
            :param view:
            :return: True有权限;False无权限
            """
            user_type_id = request.auth.user.user_type
            if user_type_id > 0:
                return True
            return False
    
        # 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无权限
            """
            user_type_id = request.auth.user.user_type
            if user_type_id > 0:
                return True
            return False
    
    
    class ManagerPermission(BasePermission):
        """
        视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
        Return `True` if permission is granted, `False` otherwise.
        :param request:
        :param view:
        :param obj:
        :return: True有权限;False无权限
        """
    
        def has_permission(self, request, view):
            user_type_id = request.auth.user.user_type
            if user_type_id > 1:
                return True
            return False
    
        # 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无权限
            """
            user_type_id = request.auth.user.user_type
            if user_type_id > 1:
                return True
            return False
    

    试图函数

    from django.shortcuts import render, HttpResponse
    
    # Create your views here.
    from django.http import JsonResponse
    from rest_framework.views import APIView
    from app01.utils.auth import HeaderAuthentication, TokenAuthtication
    from app01.utils.permission import UserPermission, ManagerPermission
    from app01 import models
    import uuid
    
    
    class AuthView(APIView):
        authentication_classes = []
    
        def post(self, request, *args, **kwargs):
            response = {'code': 1000}
            user = request.data.get('username')
            pwd = request.data.get('password')
    
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                response['code'] = 1001
                response['msg'] = '用户或密码错误'
                return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
            try:
                token = str(uuid.uuid4())
                models.UserToken.objects.update_or_create(user=obj, defaults={"token": token})
                response['token'] = token
            except Exception as e:
                print("Error: ", e)
            return JsonResponse(response, json_dumps_params={'ensure_ascii': False})
    
    
    class UserView(APIView):
        # 认证的动作是由request.user触发
        authentication_classes = [HeaderAuthentication, TokenAuthtication]
    
        # 循环执行所有的权限,当前试图只允许管理员以上权限访问
        permission_classes = [ManagerPermission, ]
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return HttpResponse('user.get: %s,token: %s' % (request.user, request.auth))
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('user.post')
    

    验证:
    [站外图片上传中...(image-8f9f77-1528210315969)]
    [站外图片上传中...(image-7d1e94-1528210315971)]

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

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.auth.HeaderAuthentication', ],
        "DEFAULT_PERMISSION_CLASSES": [
            "app01.utils.permission.ManagerPermission",
        ],
    }
    

    路由

    $ cat urls.py
    
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/(?P<version>\w+)/', include('app01.urls')),
    ]
    
    $ cat app01/urls.py 
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^auth/$', views.AuthView.as_view()),
        url(r'^user/$', views.UserView.as_view()),
    ]
    

    试图

    $ cat views.py
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    
    class UserView(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请求,响应内容')
    

    3.3 用户访问次数/频率限制

    3.3.1 基于用户IP访问限制频率

    路由

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

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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,当做请求者的唯一IP
            Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
            if present and number of proxies is > 0. If not use all of
            HTTP_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_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)
    

    3.3.2 基于用户IP控制访问频率(利用Django缓存)

    全局配置

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

    路由

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

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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):
            """
            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.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.3.3 在试图中限制请求频率

    全局配置

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

    路由

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

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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):
            """
            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.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)
    

    3.3.4 匿名时用IP限制+登录时用Token限制

    全局配置

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        'DEFAULT_THROTTLE_RATES': {
            'backend_anon': '10/m',
            'backend_user': '20/m',
        },
    }
    

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from rest_framework.throttling import SimpleRateThrottle
    
    
    class BackendAnonRateThrottle(SimpleRateThrottle):
        """
        匿名用户,根据IP进行限制
        """
        scope = "backend_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 BackendUserRateThrottle(SimpleRateThrottle):
        """
        登录用户,根据用户token限制
        """
        scope = "backend_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 = [BackendAnonRateThrottle, BackendUserRateThrottle, ]
    
        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请求,响应内容')
    

    3.3.5 全局使用

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES': [
            'api.utils.throttles.throttles.BackendAnonRateThrottle',
            'api.utils.throttles.throttles.BackendUserRateThrottle',
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '10/day',
            'user': '10/day',
            'backend_anon': '10/m',
            'backend_user': '20/m',
        },
    }
    

    3.4 版本

    共6个类

    1. BaseVersioning
    2. AcceptHeaderVersioning
    3. URLPathVersioning
    4. NamespaceVersioning
    5. HostNameVersioning
    6. QueryParameterVersioning
      而且还可以看到BaseVersioning类是其余5个类的父类,并且这其余的5个类,每个类中都有一个determine_version方法,在项目的视图函数中导入其中任意一个类,打印versioning_class
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView
    from django.views import View
    from rest_framework.versioning import QueryParameterVersioning
    
    class UsersView(APIView):
        versioning_class=QueryParameterVersioning
    
        def get(self,request,*args,**kwargs):
            print(self.versioning_class)        #打印versioning_class
    
            return HttpResponse("aaaa")
    

    输出结果:

    <class 'rest_framework.versioning.QueryParameterVersioning'>
    

    所以versioning_class是一个类,并且versioning_class类中有一个determine_version方法

    3.4.1 基于URL的GET传参方式

    如:/users?version=v1
    全局配置

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

    路由

    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import url, include
    from app01.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view(), name='test'),
    ]
    

    试图

    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请求,响应内容')
    

    3.4.2 基于URL的正则方式

    如: /v1/users/

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

    路由

    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'),
    ]
    

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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.4.3 基于accept请求头方式

    如:Accept: application/json; version=1.0

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

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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请求,响应内容,基于Accept请求头方式')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容,基于Accept请求头方式')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容,基于Accept请求头方式')
    

    3.4.4 基于主机名方式

    如: v1.example.com

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

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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请求,响应内容,基于主机名方式')
    

    3.4.5 基于Django路由系统的namespace

    如: example.com/v1/users/

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

    路由

    from django.conf.urls import url, include
    from app01.views import TestView
    
    urlpatterns = [
        url(r'^v1/', ([
                          url(r'test/', TestView.as_view(), name='test1')
                      ], None, 'v1')),
        url(r'^v2/', ([
                          url(r'test/', TestView.as_view(), name='test2')
                      ], None, 'v2')),
    
    ]
    

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import NamespaceVersioning
    
    
    class TestView(APIView):
        authentication_classes = []
        permission_classes = []
        versioning_class = NamespaceVersioning
    
        def get(self, request, *args, **kwargs):
            # 获取版本
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test1', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容,基于Django路由系统的namespace')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容,基于Django路由系统的namespace')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容,基于Django路由系统的namespace')
    

    3.4.6 全局应用

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

    3.4.7 自定义版本控制方案

    要实现自定义版本控制方案,请继承 BaseVersioning并覆盖 .determine_version 方法。
    举个栗子
    以下示例使用自定义的 X-API-Version header 来确定所请求的版本

    class XAPIVersionScheme(versioning.BaseVersioning):
        def determine_version(self, request, *args, **kwargs):
            return request.META.get('HTTP_X_API_VERSION', None)
    

    如果你的版本控制方案基于请求 URL,则还需要更改版本化 URL 的确定方式。为了做到这一点,你应该重写类的 .reverse()方法。有关示例,请参阅源代码。

    3.5 解析器(parser)

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

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

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    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请求,请求内容: %s' % request.data)
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容,解析器')
    

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

    路由

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

    试图

    #!/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 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.5.3 仅处理请求头content-type为multipart/form-data的请求体

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.parsers import MultiPartParser
    
    
    class TestView(APIView):
        authentication_classes = []
        permission_classes = []
        parser_classes = [MultiPartParser, ]
    
        def get(self, request, *args, **kwargs):
            return render(request, 'test.html')
    
        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请求,响应内容')
    

    模版文件

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

    3.5.4 仅上传文件

    路由

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

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.parsers import FileUploadParser
    
    
    class TestView(APIView):
        authentication_classes = []
        permission_classes = []
        parser_classes = [FileUploadParser, ]
    
        def get(self, request, *args, **kwargs):
            return render(request, 'test.html')
    
        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请求,响应内容')
    

    模版文件

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

    3.5.5 同时多个Parser

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

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

    试图

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

    3.5.6 全局应用

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

    路由

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

    试图

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

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

    3.6 序列化

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

    3.6.1 自定义字段

    路由

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

    models

    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, '普通用户'),
            (2, '管理员'),
            (3, '超级管理员'),
        )
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        user_type = models.IntegerField(choices=user_type_choices, default=1)
    
    
    class UserToken(models.Model):
        user = models.OneToOneField('UserInfo', on_delete=True)
        token = models.CharField(max_length=64)
    

    试图

    # !/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 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):
        user_type = serializers.IntegerField()
        username = serializers.CharField(min_length=3)
        password = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])
    
    
    class TestView(APIView):
        authentication_classes = []
        permission_classes = []
    
        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请求,响应内容')
    

    POST验证:

    curl -X POST \
      http://127.0.0.1:8001/test/ \
      -H 'Cache-Control: no-cache' \
      -H 'Content-Type: application/json' \
      -H 'Postman-Token: 4ea5d1d1-e3b1-38a3-19f6-25070ace2342' \
      -d '{
            "user_type": 2,
            "username": "python",
            "password": "666"
        }'
    

    3.6.2 基于Model自动生成字段

    路由

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

    试图

    #!/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 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.6.3 生成URL

    路由

    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'),
    ]
    

    models

    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, '普通用户'),
            (2, '管理员'),
            (3, '超级管理员'),
        )
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        user_type = models.IntegerField(choices=user_type_choices, default=1)
    
    
    class UserToken(models.Model):
        user = models.OneToOneField('UserInfo', on_delete=True)
        token = models.CharField(max_length=64)
    

    试图

    # !/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 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_type = serializers.HyperlinkedIdentityField(view_name='detail')
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
            extra_kwargs = {
                'username': {'min_length': 6},
                'pasword': {'validators': [PasswordValidator(666), ]},
            }
    
    
    class TestView(APIView):
        authentication_classes = []
        permission_classes = []
    
        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请求,响应内容')
    

    请求如下图所示:
    [站外图片上传中...(image-5aaf7-1528210315972)]

    3.6.4 自动生成URL

    路由

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

    试图

    # !/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 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='detail-info')
        tt = serializers.CharField(required=False)
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            list_serializer_class = serializers.ListSerializer
    
            extra_kwargs = {
                'username': {'min_length': 6},
                'password': {'validators': [PasswordValidator(666), ]},
                'url': {'view_name': 'detail-info'},
                'ut': {'view_name': 'detail-info'},
            }
    
    
    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请求,响应内容')
    

    3.7 分页

    3.7.1 根据页码进行分页

    路由

    from django.conf.urls import url, include
    from app01.views import UserViewSet
    
    urlpatterns = [
        url(r'test/', UserViewSet.as_view(), name='test'),
    ]
    

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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
    

    访问验证:

    http://127.0.0.1:8001/test/?page=1
    http://127.0.0.1:8001/test/?page=2
    

    3.7.2 位置和个数进行分页

    路由

    from django.conf.urls import url, include
    from app01.views import UserViewSet
    
    urlpatterns = [
        url(r'test/', UserViewSet.as_view(), name='test'),
    ]
    

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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 = 2
        # 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.7.3 游标分页

    路由

    from django.conf.urls import url, include
    from app01.views import UserViewSet
    
    urlpatterns = [
        url(r'test/', UserViewSet.as_view(), name='test'),
    ]
    

    试图

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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 = 10
        # 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
    

    3.8 路由系统

    3.8.1 自定义路由

    路由

    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())
    ]
    

    试图

    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('...')
    

    3.8.2 半自动路由

    路由

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

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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.8.3 全自动路由

    路由

    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)),
    ]
    

    试图

    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.9 试图

    3.9.1 GenericViewSet

    路由

    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'),
    ]
    

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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
    

    3.9.2 ModelViewSet(自定义URL)

    路由

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

    试图

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    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.9.3 ModelViewSet(rest framework路由)

    路由

    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)),
    ]
    

    试图

    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
    

    3.10 渲染器

    根据用户请求URL或用户可接受的类型,筛选出合适的渲染组件
    用户请求URL:

    3.10.1 Json

    访问URL:

    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()),
    ]
    

    试图:

    #!/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 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.10.2 表格

    访问URL:

    #!/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 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)
    

    3.10.3 Form表单

    访问URL:

    #!/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 JSONRenderer
    from rest_framework.renderers import AdminRenderer
    from rest_framework.renderers import HTMLFormRendere
    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)
    

    3.10.4 自定义显示模版

    访问URL:

    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()),
    ]
    

    试图

    #!/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 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')
    

    模版

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

    3.10.5 浏览器API+JSON

    访问URL:

    #!/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 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后缀来选择渲染器

    原文地址

    相关文章

      网友评论

        本文标题:什么是RESTful API以及Django RestFrame

        本文链接:https://www.haomeiwen.com/subject/llumsftx.html