zoukankan      html  css  js  c++  java
  • Django REST framework

    一、起步

    由于笔者水平有限,学习技术官方文档永远是首先被推荐的,推荐来自Django REST framework官网的快速教程

    二、序列化组件

    models:

    后面的代码将基于这几个表来做操作

     1 class BookDetailView(APIView):
     2     """
     3     针对单条数据的序列化
     4     """
     5 
     6     def get(self,request,id):
     7 
     8         book=Book.objects.filter(pk=id).first()
     9         bs=BookModelSerializers(book,context={'request': request})
    10         return Response(bs.data)
    11 
    12     def put(self,request,id):
    13         book=Book.objects.filter(pk=id).first()
    14         bs=BookModelSerializers(book,data=request.data)
    15         if bs.is_valid():
    16             bs.save()
    17             return Response(bs.data)
    18         else:
    19             return Response(bs.errors)
    20 
    21     def delete(self,request,id):
    22         Book.objects.filter(pk=id).delete()
    23 
    24         return Response()
    View Code

    序列化的方法:

    方法一: 硬传值

    class PublishView(View):
        def get(self,request):
            # 方式一:
            publish_list = list(Publish.objects.all().values("name","email"))
            # {'name': '苹果出版社', 'email': '123@qq.com'}{'name': '橘子出版社', 'email': '456@qq.com'}
            return HttpResponse(publish_list)

    方法二: model_to_dict(obj)

     1 class PublishView(View):
     2     def get(self,request):
     3         from django.forms.models import model_to_dict
     4         publish_list = Publish.objects.all()
     5         temp = []
     6         for obj in publish_list:
     7             # temp.append({
     8             #     "name":obj.name,
     9             #     "email":obj.email
    10             # })
    11             temp.append(model_to_dict(obj))
    12         print(temp, type(temp))
    13         # [{'name': '苹果出版社', 'email': '123@qq.com'}, {'name': '橘子出版社', 'email': '456@qq.com'}] <class 'list'>
    14         # [{'id': 1, 'name': '苹果出版社', 'email': '123@qq.com'}, {'id': 2, 'name': '橘子出版社', 'email': '456@qq.com'}] <class 'list'>
    15 
    16         return HttpResponse(temp)
    View Code

    方法三: serializers.serialize("json",publish_list)

    class PublishView(View):
        def get(self,request):
            from django.core import serializers
            publish_list = Publish.objects.all()
            ret = serializers.serialize("json",publish_list)
            """
            [
                {
                    "model": "app01.publish",
                    "pk": 1,
                    "fields": {
                        "name": "苹果出版社",
                        "email": "123@qq.com"
                    }
                },
                {
                    "model": "app01.publish",
                    "pk": 2,
                    "fields": {
                        "name": "橘子出版社",
                        "email": "456@qq.com"
                    }
                }
            ]
            """
            return HttpResponse(ret)
    View Code

    方法四:rest_framework  serializers

     1 from rest_framework import serializers
     2 
     3 class PublishSerializers(serializers.Serializer):
     4     name = serializers.CharField()
     5     email = serializers.EmailField()
     6 
     7 
     8 class PublishView(View):
     9     def get(self,request):
    10         # 方式四(推荐)
    11         publish_list = Publish.objects.all()
    12         ret = PublishSerializers(publish_list, many=True) # queryset
    13         # print(ret.data)
    14         # print(type(ret.data))
    15         # [OrderedDict([('name', '苹果出版社'), ('email', '123@qq.com')]),
    16         # OrderedDict([('name', '橘子出版社'), ('email', '456@qq.com')])]
    17         # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
    18         """
    19         >>>dict([("name","橘子出版社"),("email","456@qq.com")])
    20         {'name': '橘子出版社', 'email': '456@qq.com'}
    21         """
    22 
    23         return HttpResponse(ret.data)
    View Code

    终结者:ModelSerializer

    我们的 PublishSerializers 类中重复了很多包含在Publish模型类(model)中的信息。如果能保证我们的代码整洁,那就更好了。

    就像Django提供了Form类和ModelForm类一样,REST framework包括Serializer类和ModelSerializer类。

    下面我们换一个稍微复杂的有外键的模型来进行演示:

     1 class BookModelSerializers(serializers.ModelSerializer):
     2     class Meta:
     3         model = Book
     4         fields = "__all__"
     5 
     6     #publish=serializers.CharField(source="publish.pk")
     7     publish=serializers.HyperlinkedIdentityField(
     8             view_name="detailpublish",
     9             lookup_field="publish_id",
    10             lookup_url_kwarg="pk"
    11     )
    12 
    13 
    14 class BookView(APIView):
    15     def get(self,request):
    16         book_list=Book.objects.all()
    17         bs=BookModelSerializers(book_list,many=True,context={'request': request})
    18         return Response(bs.data)
    19     def post(self,request):
    20         # post请求的数据
    21         bs=BookModelSerializers(data=request.data)
    22         if bs.is_valid():
    23             print(bs.validated_data)
    24             bs.save()# create方法
    25             return Response(bs.data)
    26         else:
    27             return Response(bs.errors)
    28 
    29 class BookDetailView(APIView):
    30     """
    31     针对单条数据的序列化
    32     """
    33 
    34     def get(self,request,id):
    35 
    36         book=Book.objects.filter(pk=id).first()
    37         bs=BookModelSerializers(book,context={'request': request})
    38         return Response(bs.data)
    39 
    40     def put(self,request,id):
    41         book=Book.objects.filter(pk=id).first()
    42         bs=BookModelSerializers(book,data=request.data)
    43         if bs.is_valid():
    44             bs.save()
    45             return Response(bs.data)
    46         else:
    47             return Response(bs.errors)
    48 
    49     def delete(self,request,id):
    50         Book.objects.filter(pk=id).delete()
    51 
    52         return Response()
    View Code

     三、APIVIEW

    在CBV的基础上,视图类在继承了REST framework的 APIVIEW 后产生了新的调用方法

    request的加强:

    1 原生request支持的操作
    2 print("POST",request.POST)
    3 print("body",request.body)
    4 # print(request)
    5 print(type(request))
    6 from django.core.handlers.wsgi import WSGIRequest
    7     新的request支持的操作
    8 print("request.data",request.data)
    9 print("request.data type",type(request.data))
    View Code

    四、认证和授权

    a. 用户url传入的token认证

    from django.conf.urls import url, include
    from web.viewsimport TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(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异常
            """
            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请求,响应内容')
    views.py

    b. 请求头认证

    from django.conf.urls import url, include
    from web.viewsimport TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    
    urls.py
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(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
            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(':')
            if username == 'alex' and password == '123':
                return ('登录用户', '用户token')
            else:
                raise exceptions.AuthenticationFailed('用户名或密码错误')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            return 'Basic realm=api'
    
    
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
    views.py
    views.py

    c. 多个认证规则

    from django.conf.urls import url, include
    from web.views.s2_auth import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class Test1Authentication(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()# 默认值为:None
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            auth = request.META.get('HTTP_AUTHORIZATION', b'')
            if auth:
                auth = auth.encode('utf-8')
            else:
                return None
            print(auth,'xxxx')
            auth = auth.split()
            if not auth or auth[0].lower() != b'basic':
                raise exceptions.AuthenticationFailed('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            if username == 'alex' and password == '123':
                return ('登录用户', '用户token')
            else:
                raise exceptions.AuthenticationFailed('用户名或密码错误')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            # return 'Basic realm=api'
            pass
    
    class Test2Authentication(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()# 默认值为:None
                    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.
            """
            pass
    
    
    class TestView(APIView):
        authentication_classes = [Test1Authentication, Test2Authentication]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    d. 认证和权限

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.permissions import BasePermission
    
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(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()# 默认值为:None
                    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.
            """
            pass
    
    
    class TestPermission(BasePermission):
        message = "权限验证失败"
    
        def has_permission(self, request, view):
            """
            判断是否有权限访问当前请求
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True
    
        # GenericAPIView中get_object时调用
        def has_object_permission(self, request, view, obj):
            """
            视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :param obj: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True
    
    
    class TestView(APIView):
        # 认证的动作是由request.user触发
        authentication_classes = [TestAuthentication, ]
    
        # 权限
        # 循环执行所有的权限
        permission_classes = [TestPermission, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
    views.py
    views.py

    e. 全局使用

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

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        "DEFAULT_AUTHENTICATION_CLASSES": [
            "web.utils.TestAuthentication",
        ],
        "DEFAULT_PERMISSION_CLASSES": [
            "web.utils.TestPermission",
        ],
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class TestView(APIView):
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
    views.py
    views.py

    五、用户访问次数/频率限制

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

     urls.py
     views.py

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

     settings.py
     urls.py
     views.py

    c. view中限制请求频率

     settings.py
     urls.py
     views.py

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

     settings.py
     urls.py
     views.py

    e. 全局使用

     settings

    六、解析器(parser) 

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

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

     urls.py
     views.py

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

     urls.py
     views.py

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

     urls.py
     views.py
     upload.html

    d. 仅上传文件

     urls.py
     views.py
     upload.html

    e. 同时多个Parser

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

     urls.py
     views.py

    f. 全局使用

     settings.py
     urls.py
     views.py

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

    七、分页

    a. 根据页码进行分页

     urs.py
     views.py

    b. 位置和个数进行分页

     urls.py
     views.py

    c. 游标分页

     urls.py
     views.py

    八、路由系统

    a. 自定义路由

     urls.py
     views.py

    b. 半自动路由

     urls.py
     views.py

    c. 全自动路由

     urls.py
     views.py

    九、视图

    a. GenericViewSet

     urls.py
     views.py

    b. ModelViewSet(自定义URL)

     urls.py
     views.py

    c. ModelViewSet(rest framework路由)

     urls.py
     views.py

     参考资料:

    武佩奇:https://www.cnblogs.com/wupeiqi/articles/7805382.html
    YUAN先生: https://www.cnblogs.com/yuanchenqi/articles/8719520.html
  • 相关阅读:
    扫描线算法
    评论备份(3)
    评论备份(2)
    二分法的注意事项
    sam模板
    Machine Learning(Andrew Ng)学习笔记
    洛谷P2221 [HAOI2012]高速公路
    洛谷P3233 [HNOI2014]世界树
    P2515 [HAOI2010]软件安装
    BZOJ4293: [PA2015]Siano
  • 原文地址:https://www.cnblogs.com/huang-yc/p/9800552.html
Copyright © 2011-2022 走看看