drf 路由生成

雲崖先生發表於2020-10-31

前言

   在drf中,我們寫介面可以通過繼承modelViewSet從而達到非常快速的功能實現,這十分的方便,但是modelViewSet由於需要根據不同的引數來對應不同的處理,所以我們寫的url最少都需要兩條,如下所示:

path('api/users/', views.UserAPI.as_view(actions={"get":"list","post":"create"})),
re_path('^api/users/(?P<uid>\d+)?',views.UserAPI.as_view(actions={"get":"retrieve","patch":"update","delete":"destroy"}))

   有沒有什麼辦法能夠快速的生成兩條url呢?其實是有的,但是在這之前我們也可以對他手動封裝一個元件,達到自動生成路由的功能。

手動封裝

   下面是手動封裝url的一個示例,首先url本身就是一個列表,所以我們的元件最終可以返回一個可迭代物件就行,直接將生成的url新增至已有的url列表中。

  

from django.contrib import admin
from django.urls import path, re_path
from app01 import views
from rest_framework.routers import SimpleRouter


class GenerateRouter:
    def __init__(self):
        self.urls = None

    def register(self, prefix, viewset, basename=None):
    	# prefix 匹配規則,不用加 /
        # viewset 繼承自ModelViewSet檢視類
        # basename 別名
    
        from django.urls import path, re_path
        name = viewset.serializer_class.Meta.model.__name__.lower()  # 獲取查詢的資料表名字

        self.urls =  (
            re_path("^%s/$" % prefix, viewset.as_view(actions={"get": "list", "post": "create"}),
                 name=basename or name + "-list"),  # 不需要引數,查全部和新增

            re_path('^%s/(?P<%s>[^/.]+)/$' %(prefix,viewset.lookup_url_kwarg or "pk"),  # 如果設定捕獲欄位,就用捕獲欄位,否則就用pk
                    viewset.as_view(
                        actions={"get": "retrieve", "patch": "update", "delete": "destroy", "put": "update"}),
                    name=basename or name + "-detail"),
        )


router = GenerateRouter() # 例項化
router.register("api/users", views.UserAPI)  # 註冊

urlpatterns = [
    path('admin/', admin.site.urls),
]

urlpatterns.extend(router.urls)  # 新增

   它生成的路由是這樣子的:

^api/users/$ [name='user-list']
^api/users/(?P<uid>[^/.]+)/$ [name='user-detail']

router元件

簡單路由

   在drf中也提供了這樣的元件,它可以幫助我們快速的生成路由。比我們上面自己封裝的要強大一些,但是本質上都是一樣的。

   下面是簡單使用,首先我們需要進行匯入。

from rest_framework.routers import SimpleRouter

   其次是對其進行例項化後註冊一個路由資訊。

def register(self, prefix, viewset, basename=None)  
# prefix 匹配規則,不用加 /
# viewset 繼承自ModelViewSet檢視類
# basename 別名

from django.contrib import admin
from django.urls import path
from app01 import views

from rest_framework.routers import SimpleRouter  # 第一步匯入


router = SimpleRouter()  # 第二步例項化
router.register("api/users", views.UserAPI)  # 第三步註冊路由


urlpatterns = [
    path('admin/', admin.site.urls),
]

urlpatterns.extend(router.urls)  # 第四步新增

   它會自動生成兩條url,這與我們上面的手動封裝是一樣的原理:

^api/users/$ [name='user-list']
^api/users/(?P<uid>[^/.]+)/$ [name='user-detail']

   至於這裡為什麼是uid,這是因為我們的APIlookup_url_kwarg設定的就是uid

   可以看以下上面手動封裝中,也是這麼做的。

class UserAPI(ModelViewSet):
    queryset = models.User.objects  # 傳入物件即可
    serializer_class = ser.UserModelSerializers  # 序列化類
    lookup_field = "pk"
    lookup_url_kwarg = "uid"  # 由於捕獲的是uid,需要宣告

預設路由

   簡單的SimpleRouter()其實就足夠我們用了,但是drf也提供了更高階的預設路由,它會生成六條url,但是基本沒啥用。

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

from rest_framework.routers import SimpleRouter
from rest_framework.routers import DefaultRouter



router = DefaultRouter()
router.register("api/users", views.UserAPI)

urlpatterns = [
    path('admin/', admin.site.urls),
]

urlpatterns.extend(router.urls)

   以下是生成的路由資訊。

^api/users/$ [name='user-list']
^api/users\.(?P<format>[a-z0-9]+)/?$ [name='user-list']
^api/users/(?P<uid>[^/.]+)/$ [name='user-detail']
^api/users/(?P<uid>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='user-detail']
^$ [name='api-root']
^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']

action裝飾器

   action幹什麼用?是為了給繼承自ModelViewSet的檢視類中定義的函式也新增路由。

from . import models
from . import ser

from rest_framework.decorators import action  # 匯入裝飾器
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response


class UserAPI(ModelViewSet):
    queryset = models.User.objects.all()  # 傳入物件即可
    serializer_class = ser.UserModelSerializers  # 序列化類
    lookup_field = "pk"
    lookup_url_kwarg = "uid"  # 由於捕獲的是uid,需要宣告

    @action(methods=['GET','POST'],detail=True)
    def customize(self,request,uid):
        book=self.get_queryset()[:2]  # 從0開始擷取一條
        serializer=self.get_serializer(book,many=True)  # 只要是返回queryset物件,就需要many=True
        return Response(serializer.data)

    # action的執行會覆蓋掉本身的get、post、patch等所執行的方法,如list,update,create等預設行為。
    # methods代表請求方式,當有該種請求到來時,則執行該方法
    # detail代表是否需要捕獲引數,True則是捕獲。捕獲引數名字就是lookup_url_kwarg

   生成的路由資訊如下:

^api/users/(?P<uid>[^/.]+)/customize/$ [name='user-customize']
^api/users/(?P<uid>[^/.]+)/customize\.(?P<format>[a-z0-9]+)/?$ [name='user-customize']

相關文章