当前位置: 首页 > news >正文

Django REST framework:SimpleRouter 使用指南

1. SimpleRouter 是什么?

SimpleRouter 是 DRF(Django REST framework)提供的路由器,能根据 ViewSet 自动生成标准的 REST 路由,包括:

  • GET /resources/ → 列表(list
  • POST /resources/ → 新建(create
  • GET /resources/{lookup}/ → 详情(retrieve
  • PUT /resources/{lookup}/ → 全量更新(update
  • PATCH /resources/{lookup}/ → 局部更新(partial_update
  • DELETE /resources/{lookup}/ → 删除(destroy

SimpleRouter vs DefaultRouter

  • SimpleRouter:只生成资源路由,不包含“API 根目录”(api root) 页面。
  • DefaultRouter:在 SimpleRouter 基础上多一个“API 根目录”索引页(用于浏览器友好的入口)。

选择建议

  • 你需要简洁、纯粹的 REST 路由:SimpleRouter
  • 你希望有一个根索引页(或给产品/测试同学更友好的浏览入口):DefaultRouter

2. 快速上手(完整示例)

2.1 模型与序列化器

# app/models.py
from django.db import modelsclass Book(models.Model):isbn = models.CharField(max_length=20, unique=True)title = models.CharField(max_length=200)author = models.CharField(max_length=100)pub_date = models.DateField(null=True, blank=True)def __str__(self):return f"{self.title}({self.isbn})"
# app/serializers.py
from rest_framework import serializers
from .models import Bookclass BookSerializer(serializers.ModelSerializer):class Meta:model = Bookfields = ["id", "isbn", "title", "author", "pub_date"]

2.2 ViewSet(核心)

# app/views.py
from rest_framework import viewsets, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializerclass BookViewSet(viewsets.ModelViewSet):"""标准 CRUD + 一个自定义动作(按作者聚合数量)"""queryset = Book.objects.all()serializer_class = BookSerializerpermission_classes = [permissions.IsAuthenticatedOrReadOnly]# 将默认主键 lookup 切换为 ISBN(可选)lookup_field = "isbn"lookup_url_kwarg = "isbn"  # URL中的参数名(默认与lookup_field相同)@action(detail=False, methods=["GET"], url_path="by-author")def by_author(self, request):"""GET /books/by-author/返回每位作者的图书数量"""from django.db.models import Countdata = Book.objects.values("author").annotate(count=Count("id")).order_by("-count")return Response(list(data))

2.3 路由

# app/urls.py
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import BookViewSetrouter = SimpleRouter()
# prefix='books' 会得到 /books/ 与 /books/{isbn}/
# basename 用于反向解析名的前缀,若未传且能从 queryset.model 推断,则可省略
router.register(r"books", BookViewSet, basename="book")urlpatterns = [path("", include(router.urls)),
]
# project/urls.py(项目根 URL)
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path("admin/", admin.site.urls),path("api/v1/", include("app.urls")),  # 建议加上版本前缀
]

现在可用的路由(示例)

  • GET /api/v1/books/
  • POST /api/v1/books/
  • GET /api/v1/books/{isbn}/
  • PUT /api/v1/books/{isbn}/
  • PATCH /api/v1/books/{isbn}/
  • DELETE /api/v1/books/{isbn}/
  • GET /api/v1/books/by-author/(自定义动作)

3. register() 参数详解

router.register(prefix, viewset, basename=None)
  • prefix:URL 前缀(复数资源名,建议小写、用中划线分词如 user-profiles)。

  • viewset:继承了 ViewSet/ModelViewSet 的类。

  • basename:用于生成路由名称前缀。未提供时,DRF 会尝试从 viewset.queryset.model 推断。

    • 反向解析名形如:<basename>-list<basename>-detail<basename>-<action>

何时必须传 basename:当你的 ViewSet 没有 queryset(例如动态数据源)或无法从中推断模型时,必须显式提供,否则路由注册会报错或反向解析名缺失。


4. 路由规则与反向解析

4.1 自动生成的 URL 与名称

basename="book" 为例:

HTTP路径对应方法反向解析名
GET/books/listbook-list
POST/books/createbook-list
GET/books/{lookup}/retrievebook-detail
PUT/books/{lookup}/updatebook-detail
PATCH/books/{lookup}/partial_updatebook-detail
DELETE/books/{lookup}/destroybook-detail
GET/books/by-author/(示例)@action(detail=False)book-by-author

反向解析示例:

from django.urls import reversereverse("book-list")               # -> "/api/v1/books/"
reverse("book-detail", kwargs={"isbn": "9787111123456"})
reverse("book-by-author")          # 自定义动作(list 级别)

5. 常用配置与细节

5.1 结尾斜杠(trailing slash)

  • DRF 提供 DEFAULT_ROUTER_TRAILING_SLASH 设置控制结尾斜杠。

    • 常用取值:

      • "/":强制以斜杠结尾(如 /books/)。
      • "":不带斜杠(如 /books)。
      • "/?":可带可不带(兼容两种风格)。
  • 统一风格非常重要;否则容易出现“有时 301/404、有时匹配不到”的瑕疵。

# settings.py
REST_FRAMEWORK = {"DEFAULT_ROUTER_TRAILING_SLASH": "/",
}

与 Django 的 APPEND_SLASH 行为也有关联;团队应统一 API 风格并写入测试。

5.2 自定义主键/查找字段

class BookViewSet(ModelViewSet):lookup_field = "isbn"        # 数据库字段lookup_url_kwarg = "isbn"    # URL 参数名

如需限制匹配格式(正则),可在 Django 4+ 使用 path converters(推荐)或子类化 Router(高级用法,见 §7.3)。

5.3 命名空间与多应用拆分

# project/urls.py
urlpatterns = [path("api/v1/books/", include(("books.urls", "books"), namespace="books")),path("api/v1/users/", include(("users.urls", "users"), namespace="users")),
]# 反向解析(含命名空间)
reverse("books:book-list")

5.4 过滤、分页、权限(与路由并列的重要配置)

# settings.py
REST_FRAMEWORK = {"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticatedOrReadOnly"],"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination","PAGE_SIZE": 20,"DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend","rest_framework.filters.SearchFilter","rest_framework.filters.OrderingFilter",],
}
# app/views.py
class BookViewSet(ModelViewSet):...filterset_fields = ["author"]   # /books/?author=xxxsearch_fields = ["title", "author"]  # /books/?search=xxxordering_fields = ["pub_date", "title"]  # /books/?ordering=-pub_date

6. 自定义动作(@action)

@action 能在标准 CRUD 之外添加自定义路由。

  • detail=False(集合级别):/books/top10/
  • detail=True(单资源级别):/books/{lookup}/publish/
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import statusclass BookViewSet(ModelViewSet):queryset = Book.objects.all()serializer_class = BookSerializerlookup_field = "isbn"@action(detail=False, methods=["GET"], url_path="top10")def top10(self, request):qs = Book.objects.order_by("-pub_date")[:10]return Response(BookSerializer(qs, many=True).data)@action(detail=True, methods=["POST"], url_path="publish")def publish(self, request, isbn=None):book = self.get_object()# ... 执行业务逻辑return Response({"isbn": book.isbn, "status": "published"}, status=status.HTTP_200_OK)

反向解析名:

  • book-top10
  • book-publish

7. 进阶:定制 Router 与嵌套路由

7.1 统一前缀与版本

# project/urls.py
from rest_framework.routers import SimpleRouter
from books.views import BookViewSet
from users.views import UserViewSetrouter = SimpleRouter()
router.register(r"books", BookViewSet, basename="book")
router.register(r"users", UserViewSet, basename="user")urlpatterns = [path("api/v1/", include(router.urls)),
]

7.2 多个 Router 合并(分应用注册)

# 每个 app 内部维护自己的 router
# app_a/urls.py -> router_a.urls
# app_b/urls.py -> router_b.urls# project/urls.py
urlpatterns = [path("api/v1/", include("app_a.urls")),path("api/v1/", include("app_b.urls")),
]

7.3 自定义 Router(修改结尾斜杠、lookup 正则……)

from rest_framework.routers import SimpleRouterclass SlashOptionalRouter(SimpleRouter):trailing_slash = "/?"  # 允许有无斜杠都匹配router = SlashOptionalRouter()
router.register(r"books", BookViewSet, basename="book")

更复杂的情况(如在 URL 中匹配特定格式的 lookup),建议用 path converters(Django 原生方案)或第三方 drf-nested-routers 实现嵌套资源(/authors/{id}/books/{isbn}/)。


8. 测试(强烈建议)

# tests/test_books_api.py
import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from app.models import Book@pytest.mark.django_db
def test_book_crud_flow():client = APIClient()# Createresp = client.post(reverse("book-list"), {"isbn": "9787111123456", "title": "DRF 实战", "author": "Alice"}, format="json")assert resp.status_code == 201# Retrieveurl = reverse("book-detail", kwargs={"isbn": "9787111123456"})resp = client.get(url)assert resp.status_code == 200assert resp.data["title"] == "DRF 实战"# Custom actionresp = client.get(reverse("book-by-author"))assert resp.status_code == 200

反向解析名(如 book-list / book-detail)写测试,可避免路径硬编码带来的回归风险。


9. 常见坑与排错

  1. 反向解析失败:多半是忘记传 basename(且无法从 queryset 推断),或命名空间未匹配(namespace:name)。
  2. 偶发 301/404:团队未统一结尾斜杠策略;请用 DEFAULT_ROUTER_TRAILING_SLASH 一次性约定。
  3. lookup_field 不生效:URL 的 kwargs 名与 lookup_url_kwarg 对不上;或某处仍用默认 pk
  4. 接口未出现在路由ViewSet 方法名不规范(必须是 list/retrieve/...@action);或没有把 router.urls include 进去。
  5. 权限/认证绕过:只在某些方法上声明 permission_classes,其他方法漏配。建议在 ViewSet 级别统一声明,特殊再覆盖。
  6. 前后端联调“接口名不固定”:团队成员直接改 prefixbasename。建议写入规范并加 API 回归测试。

10. 与文档/Schema 配合(可选)

  • 如果你要自动生成 OpenAPI / Swagger:
    推荐 drf-spectaculardrf-yasg;选择 DefaultRouter 可提供一个 root 入口,但不是必须。
  • @action 标注 detailmethodsurl_path 并补充分页/参数注释,文档会更完整。

11. 生产实践建议(Checklist)

  • 按业务域拆分应用;每个 app 内部维护自己的 router,在项目层统一 api/v{n}/ 前缀。
  • 统一 DEFAULT_ROUTER_TRAILING_SLASH;与 Nginx/网关重写规则一致。
  • 所有接口用 反向解析名 做测试与内部调用(避免硬编码路径)。
  • ViewSet 严格用标准方法名(list/retrieve/...)与 @action;自定义动作只做“业务语义上的操作”,避免滥用。
  • 统一权限、限流、分页、过滤策略;默认安全,按需放开。
  • 如需嵌套资源,优先评估是否真的需要;需要时优先用 drf-nested-routers 或清晰的扁平资源 + 查询参数。

12. 速查模板

# urls.py
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import FooViewSet, BarViewSetrouter = SimpleRouter()
router.register(r"foos", FooViewSet, basename="foo")
router.register(r"bars", BarViewSet, basename="bar")urlpatterns = [path("", include(router.urls))]
# views.py
from rest_framework import viewsets, permissions
from rest_framework.decorators import action
from rest_framework.response import Responseclass FooViewSet(viewsets.ModelViewSet):queryset = Foo.objects.all()serializer_class = FooSerializerpermission_classes = [permissions.IsAuthenticated]lookup_field = "slug"@action(detail=True, methods=["POST"], url_path="enable")def enable(self, request, slug=None):foo = self.get_object()foo.enable()return Response({"ok": True})
# settings.py
REST_FRAMEWORK = {"DEFAULT_ROUTER_TRAILING_SLASH": "/","DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination","PAGE_SIZE": 20,
}

【完】


文章转载自:

http://kqgkpu0z.ryxbz.cn
http://rviF7CK8.ryxbz.cn
http://3jJzK592.ryxbz.cn
http://0ed17Qih.ryxbz.cn
http://TJNOQtfv.ryxbz.cn
http://aRCIhkwm.ryxbz.cn
http://gENFu8XH.ryxbz.cn
http://ywli6Dfs.ryxbz.cn
http://merzanpb.ryxbz.cn
http://7cy6We2d.ryxbz.cn
http://pRqcnxbL.ryxbz.cn
http://3ir6EGbH.ryxbz.cn
http://MCKNxNCJ.ryxbz.cn
http://RzQLBvCj.ryxbz.cn
http://cdz1eAFq.ryxbz.cn
http://pXnEPfKa.ryxbz.cn
http://XNuWBrYR.ryxbz.cn
http://RFtJ2LW6.ryxbz.cn
http://4ZDCe5t8.ryxbz.cn
http://Tv6S4cmL.ryxbz.cn
http://RwjbiQHV.ryxbz.cn
http://zcj04A8c.ryxbz.cn
http://rbXEHH0C.ryxbz.cn
http://rawX5y7Q.ryxbz.cn
http://mszZUBfs.ryxbz.cn
http://iB9cTuh2.ryxbz.cn
http://HqhjQ3zp.ryxbz.cn
http://67IEcB51.ryxbz.cn
http://j9LYRlao.ryxbz.cn
http://5TEgB3vG.ryxbz.cn
http://www.dtcms.com/a/368395.html

相关文章:

  • Vue3 频率范围输入失焦自动校验实现
  • 删除元素(不是删除而是覆盖)快慢指针 慢指针是覆盖位置,快指针找元素
  • 代码随想录算法训练营第三天| 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表
  • 结合机器学习的Backtrader跨市场交易策略研究
  • 前端开发vscode插件 - live server
  • 码农的“必修课”:深度解析Rust的所有权系统(与C++内存模型对比)
  • 【Python基础】 17 Rust 与 Python 运算符对比学习笔记
  • 云手机可以息屏挂手游吗?
  • 会话管理巅峰对决:Spring Web中Cookie-Session、JWT、Spring Session + Redis深度秘籍
  • 腾讯云大模型训练平台
  • iPhone17全系优缺点分析,加持远程控制让你的手机更好用!
  • 数据泄露危机逼近:五款电脑加密软件为企业筑起安全防线
  • 阿里云vs腾讯云按量付费服务器
  • DocuAI深度测评:自动文档生成工具如何高效产出规范API文档与数据库表结构文档?
  • React JSX 语法讲解
  • 工厂办公环境如何实现一台服务器多人共享办公
  • 从 0 到 1 学 sed 与 awk:Linux 文本处理的两把 “瑞士军刀”
  • VNC连接服务器实现远程桌面-针对官方给的链接已经失效问题
  • 【Web】理解CSS媒体查询
  • 编写前端发布脚本
  • 无密码登录与设备信任:ABP + WebAuthn/FIDO2
  • 消息队列-ubutu22.04环境下安装
  • Vue3源码reactivity响应式篇之EffectScope
  • 从Java全栈到前端框架:一位程序员的实战之路
  • 【Java实战㉖】深入Java单元测试:JUnit 5实战指南
  • 【AI论文】Robix:一种面向机器人交互、推理与规划的统一模型
  • C++(Qt)软件调试---bug排查记录(36)
  • yolov8部署在一台无显卡的电脑上,实时性强方案
  • Alibaba Cloud Linux 3 安装Docker
  • SQL面试题及详细答案150道(61-80) --- 多表连接查询篇