Django-DRF(路由與擴展功能)

一. 視圖集與路由的使用

  使用視圖集ViewSet,能夠將一系列邏輯相關的動做放到一個類中:前端

  • list() 提供一組數據python

  • retrieve() 提供單個數據django

  • create() 建立數據服務器

  • update() 保存數據session

  • destory() 刪除數據app

  ViewSet視圖集類再也不實現get()、post()等方法,而是實現動做 action 如 list() 、create() 等。post

  視圖集只在使用as_view()方法的時候,纔會將action動做與具體請求方式對應上。學習

  1. 經常使用的視圖集父類

  1.ViewSet

  繼承自APIView 與 ViewSetMixin做用也與APIView基本相似,提供了身份認證、權限校驗、流量管理等。url

  ViewSet主要經過繼承ViewSetMixin來實如今調用as_view()時傳入字典(如{'get':'list'})的映射處理工做。spa

  在ViewSet中,沒有提供任何動做action方法,須要咱們本身實現action方法。

  2.GenericViewSet

  使用ViewSet一般並不方便,由於list、retrieve、create、update、destory等方法都須要本身編寫,而這些方法與前面講過的Mixin擴展類提供的方法同名,因此咱們能夠經過繼承Mixin擴展類來複用這些方法而無需本身編寫。可是Mixin擴展類依賴與GenericAPIView,因此還須要繼承GenericAPIView。

  GenericViewSet就幫助咱們完成了這樣的繼承工做,繼承自GenericAPIView與ViewSetMixin,在實現了調用as_view()時傳入字典(如{'get':'list'})的映射處理工做的同時,還提供了GenericAPIView提供的基礎方法,能夠直接搭配Mixin擴展類使用。

  3.ModelViewSet

  繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

  4.ReadOnlyModelViewSet

  繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。

  下面咱們仍是經過案例,爲你們演示吧!

  首先,先建立一個子應用。

python3 manage.py startapp collect
  5. 在collect下新建序列化器類
# collect下的serializers.py文件

from students.models import Student
from rest_framework import serializers


class StudentModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = Student
        fields = ["id", "name", "age", "sex"]
        extra_kwargs = {
            "name": {"max_length": 10, "min_length": 4},
            "age": {"max_value": 150, "min_value": 0},
        }

    def validate_name(self, data):
        if data == "root":
            raise serializers.ValidationError("用戶名不能爲root!")
        return data

    def validate(self, attrs):
        name = attrs.get('name')
        age = attrs.get('age')

        if name == "alex" and age == 22:
            raise serializers.ValidationError("alex在22時的故事。。。")

        return attrs


class StudentInfoModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ["id", "name"]
  6. collect下的urls.py
from django.urls import path, re_path
from collect import views

urlpatterns = [
    # 不要在同一個路由的as_view中書寫兩個一樣的鍵的http請求,會產生覆蓋!!!
    # ViewSet
    path('student1/', views.Student1ViewSet.as_view({"get": "get_5"})),
    path('student1/get_5_girl/', views.Student1ViewSet.as_view({"get": "get_5_girl"})),
    re_path(r'^student1/(?P<pk>\d+)/$', views.Student1ViewSet.as_view({"get": "get_one"})),
    # GenericViewSet
    path('student2/', views.Student3GenericViewSet.as_view({"get": "get_5"})),
    path('student2/get_5_girl/', views.Student3GenericViewSet.as_view({"get": "get_5_girl"})),
    # GenericViewSet,能夠和模型類進行組合快速生成基本的API接口
    path("students3/", views.Student4GenericViewSet.as_view({"get": "list", "post": "create"})),
    # ModelViewSet 默認提供了5個API接口
    path("students4/", views.Student5ModelViewSet.as_view({"post": "create", "get": "list"})),
    re_path(r"^students4/(?P<pk>\d+)/$", views.Student5ModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    # ReadOnlyModelViewSet
    path("students5/", views.Student6ReadOnlyModelViewSet.as_view({"get": "list"})),
    re_path(r"^students5/(?P<pk>\d+)/$", views.Student6ReadOnlyModelViewSet.as_view({"get": "retrieve"})),

    # 一個視圖類中調用多個序列化器
    path("student8/", views.Student8GenericAPIView.as_view()),

    # 一個視圖集中調用多個序列化器
    path("student9/", views.Student9ModelViewSet.as_view({"get": "list"})),
    re_path(r"^student9/(?P<pk>\d+)/$", views.Student9ModelViewSet.as_view({"get": "retrieve"})),
]


"""
有了視圖集之後,視圖文件中多個視圖類能夠合併成一個,可是,路由的代碼就變得複雜了,
須要咱們常常在as_view方法 ,編寫http請求和視圖方法的對應關係,
事實上,在路由中,DRF也提供了一個路由類給咱們對路由的代碼進行簡寫。
固然,這個路由類僅針對於 視圖集 纔可使用。
"""

# 路由類默認只會給視圖集中的基本5個API生成地址[ 獲取一條,獲取多條,添加.刪除,修改數據 ]
from rest_framework.routers import DefaultRouter
# 實例化路由類
router = DefaultRouter()
# router.register("訪問地址前綴","視圖集類","訪問別名")
# 註冊視圖視圖集類
router.register("student7", views.Student7ModelViewSet)

# 把路由列表註冊到django項目中
urlpatterns += router.urls
  7. collect下的views.py
"""ViewSet視圖集,繼承於APIView,因此APIView有的功能,它都有,APIView沒有的功能,它也沒有"""
from rest_framework.viewsets import ViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response


class Student1ViewSet(ViewSet):
    def get_5(self, request):
        student_list = Student.objects.all()[:5]

        serializer = StudentModelSerializer(instance=student_list, many=True)

        return Response(serializer.data)

    def get_one(self, request, pk):
        student = Student.objects.get(pk=pk)

        serializer = StudentModelSerializer(instance=student)

        return Response(serializer.data)

    def get_5_girl(self, request):
        student_list = Student.objects.filter(sex=False)[:5]

        serializer = StudentModelSerializer(instance=student_list, many=True)

        return Response(serializer.data)


"""若是但願在視圖集中調用GenericAPIView的功能,則能夠採用下面方式"""
from rest_framework.generics import GenericAPIView


class Student2ViewSet(ViewSet, GenericAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def get_5(self, request):
        student_list = self.get_queryset()[:5]

        serializer = StudentModelSerializer(instance=student_list, many=True)

        return Response(serializer.data)

    def get_one(self, request, pk):
        student = self.get_object()

        serializer = StudentModelSerializer(instance=student)

        return Response(serializer.data)

    def get_5_girl(self, request):
        student_list = self.get_queryset().filter(sex=False)[:5]

        serializer = StudentModelSerializer(instance=student_list, many=True)

        return Response(serializer.data)

"""
上面的方式,雖然實現視圖集中調用GenericAPIView,可是咱們要多了一些類的繼承。
因此咱們能夠直接繼承 GenericViewSet
"""
from rest_framework.viewsets import GenericViewSet


class Student3GenericViewSet(GenericViewSet):
    serializer_class = StudentModelSerializer
    queryset = Student.objects.all()

    def get_5(self, request):
        student_list = self.get_queryset()[:5]

        serializer = self.get_serializer(instance=student_list, many=True)

        return Response(serializer.data)

    def get_5_girl(self, request):
        student_list = self.get_queryset().filter(sex=False)[:5]

        serializer = self.get_serializer(instance=student_list, many=True)

        return Response(serializer.data)

"""
在使用GenericViewSet時,雖然已經提供了基本調用數據集(queryset)和序列化器屬性,可是咱們要編寫一些基本的
API時,仍是須要調用DRF提供的模型擴展類 [Mixins]
"""
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, CreateModelMixin


class Student4GenericViewSet(GenericViewSet, ListModelMixin, CreateModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer


from rest_framework.viewsets import ModelViewSet


class Student5ModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer


# 只讀模型視圖集
from rest_framework.viewsets import ReadOnlyModelViewSet


class Student6ReadOnlyModelViewSet(ReadOnlyModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer


# 路由的使用
from rest_framework.decorators import action


class Student7ModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 指定容許哪些http請求訪問當前視圖方法
    # detail  指定生成的路由地址中是否要夾帶pk值,True爲須要
    @action(methods=["GET"], detail=False)
    def get_6(self, request):
        serilizer = self.get_serializer(instance=self.get_queryset().get(pk=6))
        return Response(serilizer.data)


"""在多個視圖類合併成一個視圖類之後,那麼有時候會出現一個類中須要調用多個序列化器"""

"""1. 在視圖類中調用多個序列化器"""
"""原來的視圖類中基本上一個視圖類只會調用一個序列化器,固然也有可能要調用多個序列化器"""
from .serializers import StudentInfoModelSerializer


class Student8GenericAPIView(GenericAPIView):
    queryset = Student.objects.all()

    # GenericAPI內部調用序列化器的方法,咱們能夠重寫這個方法來實現根據不一樣的需求來調用不一樣的序列化器
    def get_serializer_class(self):
        if self.request.method == "GET":
            # 2個字段
            return StudentInfoModelSerializer
        return StudentModelSerializer

    def get(self, request):
        """獲取全部數據的id和name"""
        student_list = self.get_queryset()

        serializer = self.get_serializer(instance=student_list, many=True)

        return Response(serializer.data)

    def post(self, request):
        """添加數據"""
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)


"""2. 在一個視圖集中調用多個序列化器"""

class Student9ModelViewSet(ModelViewSet):
    queryset = Student.objects.all()

    """要求:
            列表數據list,返回2個字段,
            詳情數據retrieve,返回全部字段,
    """
    def get_serializer_class(self):
        # 本次客戶端請求的視圖方法名  self.action
        if self.action == "list":
            return StudentInfoModelSerializer
        return StudentModelSerializer

二. 擴展功能

  爲了方便接下來的學習,咱們建立一個新的子應用 opt

python3 manage.py startapp opt

  由於接下來的功能中須要使用到登陸功能,因此咱們使用django內置admin站點並建立一個管理員.

  建立管理員之後,訪問admin站點,先修改站點的語言配置,在settings裏修改

LANGUAGE_CODE = 'zh-hans'

  1. 認證Authentication

  能夠在配置文件中配置全局默認的認證方案

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',  # session認證
        'rest_framework.authentication.BasicAuthentication',   # 基本認證
    )
}

  也能夠在每一個視圖中經過設置authentication_classess屬性來設置

  opt下的urls.py
from django.urls import path
from opt import views

urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
]
  opt下的views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser

"""用戶的認證和權限識別"""


class Demo1APIView(APIView):
    """只容許登陸後的用戶訪問"""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        """我的中心"""
        return Response("我的中心")


class Demo2APIView(APIView):
    """只容許管理員訪問"""
    permission_classes = [IsAdminUser]

    def get(self, request):
        """我的中心2"""
        return Response("我的中心2")

  2. 權限Permissions

  權限控制能夠限制用戶對於視圖的訪問和對於具體數據對象的訪問。

  • 在執行視圖的dispatch()方法前,會先進行視圖訪問權限的判斷

  • 在經過get_object()獲取具體對象時,會進行模型對象訪問權限的判斷

  內置提供的權限:

  • AllowAny 容許全部用戶

  • IsAuthenticated 僅經過認證的用戶

  • IsAdminUser 僅管理員用戶

  • IsAuthenticatedOrReadOnly 已經登錄認證的用戶能夠對數據進行增刪改操做,沒有登錄認證的只能查看數據。

  能夠在配置文件中全局設置默認的權限管理類,如:

REST_FRAMEWORK = {
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

  若是未指明,則採用以下默認配置

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)

  也能夠在具體的視圖中經過permission_classes屬性來設置。

  opt下的urls.py
urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
    # 自定義權限
    path('auth3/', views.Demo3APIView.as_view()),
]
  opt下的views.py
# 自定義權限
from rest_framework.permissions import BasePermission


class MyPermission(BasePermission):
    def has_permission(self, request, view):
        """
        針對訪問視圖進行權限判斷
        :param request: 本次操做的http請求對象
        :param view:  本次訪問路由對應的視圖對象
        :return:
        """
        if request.user.username == "xiaoming":
            return True
        return False


class Demo3APIView(APIView):
    permission_classes = [MyPermission]

    def get(self, request):
        """我的中心3"""
        return Response("我的中心3")

  3. 限流Throttling

  能夠對接口訪問的頻次進行限制,以減輕服務器壓力。

  通常用於付費購買次數,投票等場景使用.

  能夠在配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES進行全局配置

REST_FRAMEWORK = {
    # 限流
    'DEFAULT_THROTTLE_CLASSES': (  # 對全局進行設置
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/hour',
        'user': '3/minute',
    }
}

  DEFAULT_THROTTLE_RATES 可使用 second, minute, hour 或day來指明週期。

  也能夠在具體視圖中經過throttle_classess屬性來配置

  opt下的urls.py
urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
    # 自定義權限
    path('auth3/', views.Demo3APIView.as_view()),
    # 限流
    path('auth4/', views.Demo4APIView.as_view()),
]
  opt下的views.py
# 限流
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle


class Demo4APIView(APIView):
    # throttle_classes = [UserRateThrottle, AnonRateThrottle]  # 全局配置後,這裏就不用指定

    def get(self, request):
        """投票頁面"""
        return Response("投票頁面")

  4. 過濾Filtering

  對於列表數據可能須要根據字段進行過濾,咱們能夠經過添加django-fitlter擴展來加強支持。

pip3 install django-filter

  在配置文件裏進行註冊

INSTALLED_APPS = [
    ...
    'django_filters',  # 須要註冊應用,
]

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

  在視圖中添加filter_fields屬性,指定能夠過濾的字段。

  opt下的urls.py
urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
    # 自定義權限
    path('auth3/', views.Demo3APIView.as_view()),
    # 限流
    path('auth4/', views.Demo4APIView.as_view()),
    # 過濾
    path('data5/', views.Demo5APIView.as_view()),
]
  opt下的views.py
# 過濾
from rest_framework.generics import GenericAPIView, ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend


class Demo5APIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [DjangoFilterBackend]  # 全局配置後,這裏就不用指定了。
    filter_fields = ['age', "id"]  # 聲明過濾字段

  5. 排序Ordering

  對於列表數據,REST framework提供了OrderingFilter過濾器來幫助咱們快速指明數據按照指定字段進行排序。

  使用方法:

  在類視圖中設置filter_backends,使用rest_framework.filters.OrderingFilter過濾器,REST framework會在請求的查詢字符串參數中檢查是否包含了ordering參數,若是包含了ordering參數,則按照ordering參數指明的排序字段對數據集進行排序。

  前端能夠傳遞的ordering參數的可選字段值須要在ordering_fields中指明。

  opt下的urs.py
urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
    # 自定義權限
    path('auth3/', views.Demo3APIView.as_view()),
    # 限流
    path('auth4/', views.Demo4APIView.as_view()),
    # 過濾
    path('data5/', views.Demo5APIView.as_view()),
    # 排序
    path('data6/', views.Demo6APIView.as_view()),
]
  opt下的views.py
# 排序
from rest_framework.filters import OrderingFilter


class Demo6APIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [DjangoFilterBackend, OrderingFilter]  # 局部配置會覆蓋全局配置
    filter_fields = ['id', "sex"]
    ordering_fields = ['id', "age"]

  6. 分頁Pagination

  REST framework提供了分頁的支持。

  咱們能夠在配置文件中設置全局的分頁方式,如:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每頁數目
}

  也可經過自定義Pagination類,來爲視圖添加不一樣分頁行爲。在視圖中經過pagination_clas屬性來指明。

  opt下的urls.py
urlpatterns = [
    path('auth1/', views.Demo1APIView.as_view()),
    path('auth2/', views.Demo2APIView.as_view()),
    # 自定義權限
    path('auth3/', views.Demo3APIView.as_view()),
    # 限流
    path('auth4/', views.Demo4APIView.as_view()),
    # 過濾
    path('data5/', views.Demo5APIView.as_view()),
    # 排序
    path('data6/', views.Demo6APIView.as_view()),
    # 分頁
    path('data7/', views.Demo7APIView.as_view()),
]
  opt下的views.py
# 分頁
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination

"""1. 自定義分頁器,定製分頁的相關配置"""
"""
# 頁碼分頁  PageNumberPagination
前端訪問形式:GET  http://127.0.0.1:8000/opt/data7/?page=4

page=1   limit 0,10
page=2   limit 10,20

# 偏移量分頁  LimitOffsetPagination
前端訪問形式:GET  http://127.0.0.1:8000/opt/data7/?start=4&size=3

start=0  limit 0,10
start=10 limit 10,10
start=20 limit 20,10
"""


class StandardPageNumberPagination(PageNumberPagination):
    """分頁相關配置"""
    page_query_param = "page"          # 設置分頁頁碼關鍵字名
    page_size = 3                      # 設置每頁顯示數據條數
    page_size_query_param = "size"     # 設置指定每頁大小的關鍵字名
    max_page_size = 5                  # 設置每頁顯示最大值


class StandardLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2                  # 默認限制,默認值與PAGE_SIZE設置一致
    limit_query_param = "size"         # limit參數名
    offset_query_param = "start"       # offset參數名
    max_limit = 5                      # 最大limit限制


class Demo7APIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 分頁
    # 頁碼分頁類
    pagination_class = StandardPageNumberPagination
    # 偏移量分頁類
    # pagination_class = StandardLimitOffsetPagination

  注意:若是在視圖內關閉分頁功能,只需在視圖內設置

pagination_class = None
相關文章
相關標籤/搜索