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
posted @ 2018-10-16 20:48  YanceDev  阅读(418)  评论(0编辑  收藏  举报