Django REST framework

1、起步

因爲筆者水平有限,學習技術官方文檔永遠是首先被推薦的,推薦來自Django REST framework官網的快速教程html

2、序列化組件

models:python

後面的代碼將基於這幾個表來作操做web

 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

序列化的方法:django

方法一: 硬傳值json

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)api

 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  serializersapp

 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

終結者:ModelSerializeride

咱們的 PublishSerializers 類中重複了不少包含在Publish模型類(model)中的信息。若是能保證咱們的代碼整潔,那就更好了。post

就像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

 3、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

 

4、認證和受權

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

5、用戶訪問次數/頻率限制

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

 

6、解析器(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 來進行獲取

 

7、分頁

a. 根據頁碼進行分頁

  urs.py
  views.py

b. 位置和個數進行分頁

  urls.py
  views.py

c. 遊標分頁

  urls.py
  views.py

 

8、路由系統

a. 自定義路由

  urls.py
  views.py

b. 半自動路由

  urls.py
  views.py

c. 全自動路由

  urls.py
  views.py

9、視圖

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
相關文章
相關標籤/搜索