65. drf之视图集路由

1. 视图集路由

1.1 概念

Django 原生路由:path('xxx/', 视图.as_view()),http请求方式靠视图里的def get / post方法名绑定。

DRF 路由分两套体系:
  1. 普通路由:沿用 Django 原生path(),用于 APIView / GenericAPIView / 9个视图子类
  2. 视图集路由 (routers 路由器):专门给ViewSet系列视图集使用,自动生成 url 与 http‑action 映射
核心:视图集没有get/post,只有list/retrieve/create这类 action,普通 path 写起来很麻烦,于是 DRF 提供路由器批量生成路由。

1.2 普通 Django path 路由(非视图集)

用于:APIView、GenericAPIView、9 个视图子类

urlpatterns = [
    path("books/", BookListCreateView.as_view()),
    path("books/<int:pk>/", BookDetailView.as_view()),
]

规则:
  视图内部写def get(self,req),接收 GET
  视图内部写def post(self,req),接收 POST

ViewSetMixin 混入后,path 可以传动作映射字典

# 只有继承ViewSetMixin才支持 as_view({"http方法":"action"})
path("test/", TestView.as_view({"post":"login","get":"list"}))

1.3 DRF 路由器(routers),专门给视图集

[1] 两个路由器

(1) SimpleRouter

只生成资源接口,不生成根页面浏览入口

from rest_framework.routers import SimpleRouter
router = SimpleRouter()
# 注册:路径,视图集
router.register(r'books', BookView)

urlpatterns = [
    # 其他普通path
]
# 把router生成的url追加进去
urlpatterns += router.urls

如果有多个视图类,则需要注册多次

生成的路由表:

请求urlaction
GET /books/ list
POST /books/ create
GET /books/{pk}/ retrieve
PUT /books/{pk}/ update
PATCH /books/{pk}/ partial_update
DELETE /books/{pk}/ destroy

(2) DefaultRouter(SimpleRouter 的子类)

在 SimpleRouter 基础上,多了一个根路径/,访问根地址会返回一个可浏览 API 页面,展示所有接口列表,开发调试更友好,开发阶段常用。

from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'books', BookView)
urlpatterns += router.urls

DefaultRouter 多的根页面只是调试页面,不影响接口。

[2] 自动生成的路由注册到总路由的两种方式

方式一:
使用urlpatterns += router.urls将路由对象的URL配置列表添加到现有的URL配置中。

方式二
  使用include()函数创建URL配置
  并将路由对象作为参数传递给include()函数。
  例如,使用path("api/v1/", include(router.urls))将路由对象的URL配置嵌套在前缀为"api/v1/"的URL路径下。

from django.urls import path, include

urlpatterns = [
    path("api/v1/", include(router.urls))
]
如果不需要在总路由之后加额外路径,引号里为空即可

[3] register 参数详解

router.register(prefix, viewset, basename=None)

prefix:url 前缀,不要带斜杠开头,例r'books'
viewset:视图集类(不要加as_view())
basename:路由别名,用于reverse("basename‑list")反向解析;不写时会尝试从模型自动推导,推导失败必须手动指定 basename。

易错:router.register(r'books', BookViewSet.as_view()) ❌ 错误,不要写 as_view ()

router.register(r'books', BookModelViewSet)
✅最终生成的基础路径就是 books/
prefix 写 r'books',不要写首尾斜杠,router 内部会自动补充尾部的 /。
生成两条基础资源入口:
集合接口:books/ (list、create)
单条资源接口:books/<pk>/ (retrieve、update、destroy)

[4] 自动生成路由总结

以上自动生成的路由只适用于请求的5个函数

path('book/<int:pk>/', BookView.as_view({'delete': 'destroy', 'get': 'retrieve', 'put': 'update'})),
path('book/', BookView.as_view({'get': 'list', 'post': 'create'})),

如果视图类中有自定义的函数,则需要使用action装饰器才能自动生成路由

[4] @action 自定义函数的路由规则

视图集上用@action装饰器写自定义接口函数,路由器会自动生成 url。

from rest_framework.decorators import action

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # detail=True:需要pk,操作单个
    # url:/books/{pk}/buy/
    @action(methods=["post"], detail=True)
    def buy(self, request, pk=None):
        ...

    # detail=False:操作所有,不需要pk
    # url:/books/login/  以get方式访问这个url会触发login函数的运行
    @action(methods=["get"], detail=False)
    def login(self, request):
        ...

methods允许的请求方式可以有多个

detail=True → 格式:prefix/{pk}/方法名/
detail=False → 格式:prefix/方法名/

正常访问 books/2/buy/ → pk=2,覆盖掉默认的None
如果路由没传 pk,就拿到None,不会直接抛参数缺失报错。
如果写成 def buy(self, request, pk): 不写=None
万一路由配置出错,没有传入 pk,程序直接抛参数异常。写pk=None更加安全。
detail=False:url没有 pk,方法就不要加 pk 参数。

可以自定义 url 路径:

  不写url_path则以函数名为路径,写了url_path则以url_path为路径

@action(detail=True, url_path="my‑buy")
def buy(self,request,pk):
    # url变成 /books/{pk}/my‑buy/

[5] 手动给视图集写 path(不使用 router)

router 本质就是批量生成下面这些 path,完全可以手写:

urlpatterns = [
    path("books/", BookViewSet.as_view({"get":"list","post":"create"})),
    path("books/<int:pk>/", BookViewSet.as_view({"get":"retrieve","put":"update","delete":"destroy"})),
]

接口多的时候手写会非常繁琐,所以一般交给 router。

[6] 总结

1. ✅普通视图(APIView、GenericAPIView、9 个子类):直接用path(..., XXX.as_view()),不要用 router。
2. ✅视图集 ViewSet/ModelViewSet:优先router.register(),不要手写as_view()。
3. ❌router.register不要传as_view(),直接传类。
4. basename反向解析报错:手动设置basename="book"。
5. @action(detail=True)接口 url 必须携带 pk;detail=False不能带 pk。
6. DefaultRouter比SimpleRouter多一个可浏览 API 根页面,仅调试用。

[7] 补充

1. 当次请求的request对象,可以在视图类中获取到

def register(self, request):
    print(self.request is request)
    return Response('register')

在视图类中获取request对象的作用是:重写视图类中一些方法,某些参数需要从request中取,而方法没传入request对象

def get_serializer_class(self):
    if self.request.methos == 'get':
        ...

def perform_create(self, serializer):
    # 打印出请求体中数据
    print(self.request.body)

2.  从视图类的对象取出action

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get_serializer_class(self):
        print('---',self.request.method)
        print('---',self.request.get_full_path())
        print('---',self.action)
        return BookSerializer

    @action(methods=['GET'], detail=False) #http://127.0.0.1:8000/api/v1/app01/book/login/--->get请求
    def login(self,request):
        serializer=self.get_serializer()
        return Response('get请求打印的信息')

self.action 是个字符串,它就是不同请求方式对应的自定义函数名
用以区分不同的请求方式,使用不同的序列化类

 

posted @ 2026-08-17 17:37  pythondjango  阅读(6)  评论(0)    收藏  举报