Django Rest Framework

REST framework:

    前提小知识:

  1.Django CBV:

      URL:url(r'^/index',view.StudentView.as_view())

      view: class StudentView(view):

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

                  pass

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

                  pass

                ...

  流程:request -> 路由 -> as_view() -> view() ->dispath() (基于反射进行路由分发)

  2. [item() for item [Foo,Bar]]  # 实例化两个类的实例,放入列表中  

  3. return JsonResponse(result, status=200) 使用JsonRespone 自动json.dumps(),from django.http import JsonResponse

  4. csrf_token 中间件是在process_view中执行,1.加上装饰器@csrf_exempt 免除认证 2.装饰器@csrf_protect 添加csrf认证

  5.中间件的过程:请求-> process_request1-3 ->路由 ->process_view1-3 -> 视图 -> process_exception3-1 -> process_response3-1 ->响应

      还有一个process_template_XXX

  6.请求头有哪些:

      HOST,Cookie,eferer,Accept-Language,Accept-Encoding,User-Agent

  7.响应头:

      Allow允许的请求方式,Content-Encoding,Content-Length,Content-Type,Date,Expires,Refresh,Server,Set-Cookie,Location,Last-Modified

  8.remote_addr = request.META.get('REMOTE_ADDR')获取请求IP

  9.request.body 中的值,只有在Content-type:application/x-www-form-urlencoded 并且是post提交时data为name='mihon'&age=18这种数据类型,

      自动将request.body值转换传送给request.POST

    概述:restful代表的是一种软件架构风格,与技术无关,表征状态转移,将前后端分离,它将分布在网络中某个节点的资源通过

        URL进行标识,客户端应用通过URL来获取资源的表征,获取这些表征致使这些应用状态改变。

       

        http方法      资源操作    幂等      安全

        GET             SELSET      Y        Y

        POSE            INSERT      N        N

        PUT             UPDATE      Y        N

        DELETE          DELETE      Y        N

       

        幂等性:对同一REST接口的多次访问,得到的资源状态是相同的

        安全性:对改REST接口访问,不会使服务器端资源的状态发生改变

    retful API 设计规范:

  1.API与用户的通信协议,建议使用HTTPs(安全,需要证书但是需要钱)

  2.域名

      .https://api.example.com  将api部署在专用域名(会存在跨域问题)

      .https://example.com/api   添加api到路由上

  3.版本

      .url https://example.com/api/v1/

      .请求头                    跨域时,引发发送多次请求

  4.路径

      .https://example.com/api/v1/zoos  均使用名词表示(可以复数)

  5.请求方法

      .GET    获取资源

      .POST   新建资源

      .PUT    更新资源,完整更新

      .PATCH  更新资源,改变属性

      .DELETE 删除资源

  6.过滤

      https://example.com/api/v1/zoos?limit=10&page=2

  7.状态码

      200     ok,请求成功

      201     新建或修改数据成功

      202     表示一个请求已经进入后台排队

      204     用户数据删除成功

     

      400     用户请求错误,没有进行数据的新建或者修改

      401     表示用户没有权限

      403     表示用户没有权限,访问被禁止forbidden csrf_token

      404     表示用户请求的资源不存在

      410     用户请求的资源被永久删除

      422     当创建一个对象是,发生一个验证错误

     

      500     服务器发生错误,

  8.错误处理

      返回错误时error当作key{error:"Invalid API key"}

  9.返回结果,针对不同操作

      /collection 返回资源对象的列表

      /collection/resource 返回当个资源对象

  10.返回结果中提供详细页面的链接

      {

          1:"apple"

          detail:"https://order/apple/34"

      }

     

    基于Django实现:

        class TestView(APIView):

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

                """

                请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法

                注意:APIView中的dispatch方法有好多好多的功能

                """

                return super().dispatch(request, *args, **kwargs) #执行父类的dispath方法,将request进行封装

    用户认证:

        流程:需要创建一个CBV

            1.url(r'^test/', TestView.as_view()),]

            2.创建一个TestAuthentication(BaseAuthentication)的类并且继承BaseAuthentication

            3.TestAuthentication 实现两个方法 authenticate,authenticate_header(用于认证错误时)

            4.创建一个CBV类TestView(APIView)继承APIView

            5.如果没有在setting中设置全局配置,需要在CBV中添加 authentication_classes = [TestAuthentication, ]

            6.全局配置,在setting中REST_FRAMEWORK ={}添加两个键值对:

                1.当认证通过没有返回元组值时,返回设置的值None(默认是匿名用户)

                2.添加认证类的全路径"DEFAULT_AUTHENTICATION_CLASSES":["web.utils.TestAuthentication",]

                

        url:urlpatterns = [

            url(r'^test/', TestView.as_view()),]

        view:

            from rest_framework.views import APIView

            from rest_framework.response import Response

            from rest_framework.authentication import BaseAuthentication

            from rest_framework.request import Request

            from rest_framework import exceptions

            token_list = [

                'sfsfss123kuf3j123',

                'asijnfowerkkf9812',

            ]

   class TestAuthentication(BaseAuthentication):

       def authenticate(self, request):

           """

           用户认证,如果验证成功后返回元组: (用户,用户Token) 返回后可以通过 request.user request.auth 调用

           :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异常

           """

           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 the

           authentication scheme should return `403 Permission Denied` responses.

           """

           # 验证失败时,返回的响应头WWW-Authenticate对应的值

           pass

   class TestView(APIView):

       authentication_classes = [TestAuthentication, ]

       permission_classes = []

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

           print(request.user)

           print(request.auth)

           return Response('GET请求,响应内容')

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

           return Response('POST请求,响应内容')

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

           return Response('PUT请求,响应内容')

        

   全局使用:在setting 中配置,并且认证函数和view函数分离,在view函数中就可以不需要写authentication_classes = [TestAuthentication, ]

       但是,login视图是不需要走认证的因此authentication_classes = []写一个空列表就可以。

       REST_FRAMEWORK = {

           'UNAUTHENTICATED_USER': None,

           'UNAUTHENTICATED_TOKEN': None,

           "DEFAULT_AUTHENTICATION_CLASSES": [

               "web.utils.TestAuthentication",  # 需要写全路径,根目录为当前工程的顶级目录

           ],

           "DEFAULT_PERMISSION_CLASSES": [

               "web.utils.TestPermission",

           ],

       }

                           

    权限设置:

        1.写一个权限类 TestPermission(BasePermission)继承BasePermission

        2.添加一个静态字段message="权限认证失败",当权限认证失败,has_permission()返回False时抛异常,值为message设置的值

        3.实现一个两个方法:

            1.has_permission()权限认证成功返回True 认证失败返回Flse

            2.has_obj_permission()视图继承GenericAPIView,并在其中使用get_object获取对象,触发单独对象权限认证

        4.在CBV中添加静态字段permission_classes = [TestPermission,]

        5.全局配置"DEFAULT_PERMISSION_CLASSES": ["web.utils.TestPermission",]

           

    节流/频率限制:

        1.添加频率限制类 LuffyAnonRateThrottle(SimpleRateThrottle)继承SimpleThrottle

        2.如果需要在setting中全局配置,在LuffyAnonRateThrottle类中添加静态字段 scope = "luffy_anon"

        3.在频率限制类中实现两个方法和一个字段:

            scope = 'luffy_anon' #在源码中rate会获取scope的值,没有就会抛出异常

            1.def get_ident(self, request):

                """

                认证成功时:request.user是用户对象;request.auth是token对象

                :param request:

                :return:

                """

                # return request.auth.token

                return "user_token"  #使用user_token限制

               

            2.def get_cache_key(self, request, view): 必须重写

                # 用户已登录,则跳过 匿名频率限制

                if request.user:

                    return None   #放回None 表示不做限制

                return self.cache_format % {

                    'scope': self.scope,

                    'ident': self.get_ident(request) #如果没有实现get_ident(self, request)方法就使用继承类的IP限制

                } #需要返回 self.cache_format   

        4.如果没有在setting中设置全局设置,需要在view中throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

        5.自定制超过频率返回中文错误,在view中添加

                def throttled(self, request, wait):

                    """

                    访问次数被限制时,定制错误信息

                    """

                    class Throttled(exceptions.Throttled):

                        default_detail = '请求被限制.'

                        extra_detail_singular = '请 {wait} 秒之后再重试.'

                        extra_detail_plural = '请 {wait} 秒之后再重试.'

                    raise Throttled(wait)

        6.全局setting中设置:

            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',          #频率设置

            },}

       

    版本:

 1.基于url eg:/users?version=v1

     .setting设置

         REST_FRAMEWORK = {

             'DEFAULT_VERSION': 'v1',            # 默认版本

             'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

             'VERSION_PARAM': 'version'          # URL中获取值的key

         }

     .view中添加字段versioning_class = QueryParameterVersioning #不能是列表,QueryParameterVersioning 是URL的类

 2.基于URL正则 eg:/v1/users/

     .setting设置

         REST_FRAMEWORK = {

             'DEFAULT_VERSION': 'v1',            # 默认版本

             'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

             'VERSION_PARAM': 'version'          # URL中获取值的key

         }

     .url路由正则:

         url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),

     .view中添加字段 versioning_class = URLPathVersioning # URLPathVersioning是URL正则的类

 3.基于accept请求头 eg: Accept: application/json; version=1.0

     .setting设置

         REST_FRAMEWORK = {

             'DEFAULT_VERSION': 'v1',            # 默认版本

             'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本

             'VERSION_PARAM': 'version'          # URL中获取值的key

         }               

     .view中添加字段  versioning_class = AcceptHeaderVersioning # AcceptHeaderVersioning请求头的类

    

 4.基于主机 eg: v1.example.com

     .setting设置同上

     .view中添加字段 versioning_class = HostNameVersioning # HostNameVersioning 从主机上获取version的类

    

 5.基于Django的namespace

     .versioning_class = NamespaceVersioning

 6.全局设置:

     REST_FRAMEWORK = {

     'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", #路径

     'DEFAULT_VERSION': 'v1',

     'ALLOWED_VERSIONS': ['v1', 'v2'],

     'VERSION_PARAM': 'version' }

    

 7.使用

     class TestView(APIView):

     versioning_class = NamespaceVersioning     

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

         # 获取版本

         print(request.version)

         # 获取版本管理的类

         print(request.versioning_scheme)

         # 反向生成URL

         reverse_url = request.versioning_scheme.reverse('test', request=request)

         print(reverse_url)

         return Response('GET请求,响应内容')

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

         return Response('POST请求,响应内容')

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

         return Response('PUT请求,响应内容')

        

    解析器(parser):

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

            parser_classes = [JSONParser, FormParser, MultiPartParser, ]

        .全局使用

            REST_FRAMEWORK = {

                'DEFAULT_PARSER_CLASSES':[

                    'rest_framework.parsers.JSONParser'     #请求头content-type:application/json

                    'rest_framework.parsers.FormParser'     #请求头content-type:application/x-www-form-urlencoded

                    'rest_framework.parsers.MultiPartParser' #请求头content-type:multipart/form-data

                ]}

        .使用:request.data 转换的数据被封装到request中

      

    序列化:

               

   1. ll = serializers.HyperlinkedIdentityField(view_name='xxxx') detail使用地址链接, context={'request': request}

      

  

   from rest_framework.views import APIView

   from rest_framework.response import Response

   from rest_framework import serializers

   from .. import models

   class PasswordValidator(object):

       def __init__(self, base):

           self.base = str(base)

       def __call__(self, value):

           if value != self.base:

               message = 'This field must be %s.' % self.base

               raise serializers.ValidationError(message)

       def set_context(self, serializer_field):

           """

           This hook is called by the serializer instance,

           prior to the validation call being made.

           """

           # 执行验证之前调用,serializer_fields是当前字段对象

           pass

   class ModelUserSerializer(serializers.HyperlinkedModelSerializer):

       ll = serializers.HyperlinkedIdentityField(view_name='xxxx')

       tt = serializers.CharField(required=False)

       class Meta:

           model = models.UserInfo

           fields = "__all__"

           list_serializer_class = serializers.ListSerializer

           #  fields = ['user', 'pwd', 'ut']

           #depth = 2

           extra_kwargs = {

               'user': {'min_length': 6},

               'pwd': {'validators': [PasswordValidator(666), ]},

               'url': {'view_name': 'xxxx'},

               'ut': {'view_name': 'xxxx'},

           }

   class TestView(APIView):

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

           # # 序列化,将数据库查询字段序列化为字典

           data_list = models.UserInfo.objects.all()

           ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})

           # # 如果Many=True

           # # 或

           # # obj = models.UserInfo.objects.all().first()

           # # ser = UserSerializer(instance=obj, many=False)

           return Response(ser.data)

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

           # 验证,对请求发来的数据进行验证

           print(request.data)

           ser = ModelUserSerializer(data=request.data)

           if ser.is_valid():

               print(ser.validated_data)

           else:

               print(ser.errors)

           return Response('POST请求,响应内容')

               

    分页:

   1.根据数据和页码分页

   class StandardResultsSetPagination(PageNumberPagination):

       # 默认每页显示的数据条数

       page_size = 1

       # 获取URL参数中设置的每页显示数据条数

       page_size_query_param = 'page_size'

       # 获取URL参数中传入的页码key

       page_query_param = 'page'

       # 最大支持的每页显示的数据条数

       max_page_size = 1

      

  2.根据位置和个数进行分页

  class StandardResultsSetPagination(LimitOffsetPagination):

       # 默认每页显示的数据条数

       default_limit = 10

       # URL中传入的显示数据条数的参数

       limit_query_param = 'limit'

       # URL中传入的数据位置的参数

       offset_query_param = 'offset'

       # 最大每页显得条数

       max_limit = None

      

  3.根据游标进行分页

  class StandardResultsSetPagination(CursorPagination):

       # URL传入的游标参数

       cursor_query_param = 'cursor'

       # 默认每页显示的数据条数

       page_size = 2

       # URL传入的每页显示条数的参数

       page_size_query_param = 'page_size'

       # 每页显示数据最大条数

       max_page_size = 1000

       # 根据ID从大到小排列

       ordering = "id"

      

  class 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

    渲染器:

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

 1.Json渲染器

     http://127.0.0.1:8000/test/?format=json

     http://127.0.0.1:8000/test.json

     renderer_classes = [JSONRenderer, ]

        

 2.Adnin表格渲染器

     http://127.0.0.1:8000/test/?format=admin

     http://127.0.0.1:8000/test.admin

     renderer_classes = [AdminRenderer, ]

    

 3.From表单渲染器

     http://127.0.0.1:8000/test/?format=form

     http://127.0.0.1:8000/test.form

     renderer_classes = [HTMLFormRenderer, ]

    

 4.自定义模板

     http://127.0.0.1:8000/test/?format=html

     http://127.0.0.1:8000/test.html

     APIView:return Response(ser.data, template_name='user_detail.html')

5.浏览器格式API+JSONParser

     http://127.0.0.1:8000/test/?format=api

     http://127.0.0.1:8000/test.api

      

    路由系统:

   半自动:

       url(r'^test/(?P<pk>d+)/$', s10_generic.UserViewSet.as_view(

           {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),

          

   全自动:

       router = routers.DefaultRouter()

       router.register(r'users', s10_generic.UserViewSet)

       urlpatterns = [

           url(r'^', include(router.urls)),

       ]

原文地址:https://www.cnblogs.com/mihon/p/8980947.html