news 2026/8/6 6:45:54

DRF视图与路由进阶:从APIView到ViewSet的优雅架构设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DRF视图与路由进阶:从APIView到ViewSet的优雅架构设计

1. 项目概述:从“能跑就行”到“优雅高效”的DRF视图与路由进阶

刚接触Django REST framework (DRF) 时,很多朋友(包括当年的我)最容易陷入一个误区:视图和路由,不就是把数据从数据库里拿出来,然后通过一个URL地址扔出去吗?搞那么复杂干嘛?于是,最常见的“速成”代码诞生了:一个继承自APIView的类,里面塞满getpost方法,然后在urls.py里用path手动绑定一下。项目初期,这确实“能跑”。但随着接口数量从个位数膨胀到几十上百个,你会发现代码里充斥着重复的权限判断、序列化逻辑、分页处理,以及那个越来越像“迷宫”的urls.py文件。维护成本呈指数级上升,每次加新功能都战战兢兢。

这就是为什么我们需要系统地学习DRF的视图和路由部分。它远不止是“让接口能通”的工具,而是一套用于构建可维护、可扩展、符合RESTful规范的Web API的完整设计哲学和最佳实践工具箱。视图决定了如何处理请求和生成响应,是业务逻辑的核心载体;路由则负责将特定的URL请求精准地分发到对应的视图,是API的门面。掌握它们,意味着你能从“写功能”的层面,跃升到“设计API架构”的层面。无论是构建一个仅供内部使用的微服务,还是一个面向千万级用户开放的开放平台,清晰、健壮的视图与路由设计都是地基。接下来,我将结合近十年的踩坑经验,带你从最基本的类视图,一直深入到视图集和路由器的自动化魔法,并分享那些官方文档不会告诉你的“实战避坑指南”。

2. 视图部分:从功能实现到架构设计

DRF的视图层提供了多种抽象级别,你可以根据项目的复杂度和团队习惯选择合适的工具。理解它们之间的区别和适用场景,是写出优雅代码的第一步。

2.1 基石:APIView 与通用视图类

APIView是DRF所有视图类的基类,它继承了Django的View类,但用DRF的RequestResponse对象替换了Django原生的HttpRequestHttpResponse,并内置了认证、权限、限流等组件的调度入口。

from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Article from .serializers import ArticleSerializer class ArticleListAPIView(APIView): """ 一个基于 APIView 的文章列表视图。 手动处理了 GET 和 POST 请求。 """ def get(self, request, format=None): # 1. 从数据库获取数据 articles = Article.objects.all() # 2. 序列化数据 serializer = ArticleSerializer(articles, many=True) # 3. 返回响应 return Response(serializer.data) def post(self, request, format=None): # 1. 反序列化请求数据 serializer = ArticleSerializer(data=request.data) # 2. 验证并保存 if serializer.is_valid(): serializer.save() # 3. 返回创建成功的响应 return Response(serializer.data, status=status.HTTP_201_CREATED) # 4. 验证失败,返回错误信息 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

为什么选择 APIView?当你的接口逻辑非常独特,或者需要高度定制化的请求/响应处理流程时,APIView提供了最大的灵活性。你可以完全控制每一个步骤。

但更多时候,我们对资源的操作是标准的CRUD(创建、读取、更新、删除)。为每一个资源都手动编写getpostputpatchdelete方法,会引入大量重复代码。这时,DRF提供的通用视图类就派上用场了。

generics.ListCreateAPIViewgenerics.RetrieveUpdateDestroyAPIView是最常用的一对。它们将常见的“列表+创建”和“详情+更新+删除”模式封装好了。

from rest_framework import generics from .models import Article from .serializers import ArticleSerializer class ArticleListCreateView(generics.ListCreateAPIView): """ 使用 ListCreateAPIView 替代上面的 ArticleListAPIView。 它自动提供了 GET(列表)和 POST(创建)方法。 """ queryset = Article.objects.all() # 指定查询集 serializer_class = ArticleSerializer # 指定序列化器 class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView): """ 使用 RetrieveUpdateDestroyAPIView。 它自动提供了 GET(详情)、PUT(全量更新)、PATCH(部分更新)、DELETE(删除)方法。 """ queryset = Article.objects.all() serializer_class = ArticleSerializer # 默认使用主键pk作为查找字段,也可以通过 lookup_field 属性修改。

实操心得:queryset 与 get_queryset() 的选择你可能会注意到,上面直接设置了queryset = Article.objects.all()。这在简单场景下没问题。但在实际项目中,数据过滤是常态,比如用户只能看自己创建的文章。这时,你应该重写get_queryset(self)方法。

class UserArticleListView(generics.ListAPIView): serializer_class = ArticleSerializer def get_queryset(self): """ 重写此方法,动态返回查询集。 这是实现权限过滤、搜索、多租户隔离等功能的黄金位置。 """ # 假设我们通过JWT等认证方式,能从request中获取当前用户 user = self.request.user # 只返回当前用户创建的文章 return Article.objects.filter(author=user)

注意:直接使用queryset属性时,DRF会在类级别计算一次查询集(如Article.objects.all()),然后在每个请求中复用。如果你在get_queryset中根据请求参数动态过滤,务必确保返回的是一个新的QuerySet对象,而不是修改类级别的queryset,否则会导致跨请求的数据污染。安全起见,对于需要动态过滤的场景,一律使用get_queryset方法

2.2 飞跃:ViewSet 与 ModelViewSet

视图集(ViewSet)是DRF中一个更高级的抽象。它不像APIView那样将方法对应到HTTP动词(getpost),而是对应到资源上的操作(listcreateretrieveupdatepartial_updatedestroy)。这种抽象带来的最大好处是:可以与路由器(Router)完美配合,自动生成URL配置,极大减少了urls.py中的样板代码。

ModelViewSet是最“全能”的视图集,它继承了GenericAPIView,并混入了所有的基本操作类,默认提供了完整的CRUD端点。

from rest_framework import viewsets from .models import Article from .serializers import ArticleSerializer class ArticleViewSet(viewsets.ModelViewSet): """ 仅仅6行代码,就提供了针对Article模型的所有标准API端点: - GET /articles/ -> list (列表) - POST /articles/ -> create (创建) - GET /articles/{id}/ -> retrieve (详情) - PUT /articles/{id}/ -> update (全量更新) - PATCH /articles/{id}/ -> partial_update (部分更新) - DELETE /articles/{id}/ -> destroy (删除) """ queryset = Article.objects.all() serializer_class = ArticleSerializer

为什么这是飞跃?

  1. 代码极简:一个类搞定一个资源的所有标准操作。
  2. 路由自动化:配合路由器,无需手动编写URL模式。
  3. 逻辑集中:所有相关操作都在一个类里,维护方便。
  4. 灵活定制:你可以通过重写listcreate等方法,或使用@action装饰器添加自定义端点,在享受便利的同时保留定制能力。

踩过的坑:ModelViewSet 的“过度自动化”ModelViewSet太方便了,以至于新手容易滥用。它默认开放了所有操作。如果你的“文章”资源不允许删除(逻辑删除),或者创建需要额外的权限校验,你就必须显式地关闭或重写这些方法。

class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer # 示例1:禁用 destroy 方法(不允许删除) def destroy(self, request, *args, **kwargs): # 可以直接返回 405 Method Not Allowed return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) # 或者,更常见的做法是重写 perform_destroy 来实现逻辑删除 # instance = self.get_object() # instance.is_deleted = True # instance.save() # 示例2:在创建前加入自定义逻辑 def perform_create(self, serializer): # 在保存之前,可以注入当前用户等信息 serializer.save(author=self.request.user)

2.3 灵魂:@action 装饰器与自定义端点

标准CRUD满足不了所有业务需求。比如,我们想给文章添加“点赞”、“收藏”或“发布”等操作。这些操作不直接对应资源的增删改查,而是资源的“动作”。DRF提供了@action装饰器来优雅地处理这类需求。

from rest_framework.decorators import action from rest_framework.response import Response class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer @action(detail=True, methods=['post']) def like(self, request, pk=None): """ 点赞文章。 对应URL: /articles/{pk}/like/ detail=True 表示这是针对单个实例的操作。 methods=['post'] 定义允许的HTTP方法。 """ article = self.get_object() user = request.user # 业务逻辑:检查是否已点赞,然后创建或删除点赞关系 # ... 此处省略具体实现 ... article.like_count += 1 article.save() return Response({'status': 'liked', 'count': article.like_count}) @action(detail=False, methods=['get']) def recent(self, request): """ 获取最近发布的文章列表(自定义列表端点)。 对应URL: /articles/recent/ detail=False 表示这是针对整个集合的操作。 """ recent_articles = self.get_queryset().order_by('-created_at')[:10] serializer = self.get_serializer(recent_articles, many=True) return Response(serializer.data)

@action的核心参数解析:

  • detail:布尔值,最重要!True表示操作对象是单个资源实例(URL中包含pk),如/articles/1/like/False表示操作对象是整个资源集合,如/articles/recent/
  • methods: 一个列表,指定允许的HTTP方法,如['post']['get', 'post']
  • url_path: 自定义URL路径。如果不指定,默认使用方法名(如like)。你可以通过url_path='upvote'将其改为/articles/{pk}/upvote/
  • url_name: 给这个操作生成的URL模式起个名字,用于反向解析。

实操心得:自定义端点的序列化器对于like这种操作,返回的往往不是完整的文章对象,而是一个简单的状态消息。你可以为这个动作指定一个专用的序列化器。

class LikeActionSerializer(serializers.Serializer): status = serializers.CharField() count = serializers.IntegerField() class ArticleViewSet(viewsets.ModelViewSet): # ... 其他代码 ... @action(detail=True, methods=['post'], serializer_class=LikeActionSerializer) def like(self, request, pk=None): article = self.get_object() # ... 业务逻辑 ... # 使用指定的序列化器返回数据 serializer = self.get_serializer(data={'status': 'liked', 'count': article.like_count}) serializer.is_valid(raise_exception=True) # 通常对于输出,这步不是必须,但保持习惯 return Response(serializer.data)

3. 路由部分:从手动映射到自动注册

视图定义好了,如何让外部通过URL访问到它们?这就是路由的工作。DRF提供了强大的路由器,能将ViewSet自动映射成一系列标准的URL模式。

3.1 手动路由:path 与 ViewSet.as_view()

在深入路由器之前,理解底层的手动映射是必要的。对于APIView或基于函数的视图,我们使用Django的path

# urls.py (项目根目录或app目录) from django.urls import path from .views import ArticleListAPIView, ArticleDetailAPIView urlpatterns = [ path('articles/', ArticleListAPIView.as_view(), name='article-list'), path('articles/<int:pk>/', ArticleDetailAPIView.as_view(), name='article-detail'), ]

对于ViewSet,我们需要手动将其动作映射到HTTP方法。ViewSet类提供了一个as_view()方法,它接受一个字典,将HTTP方法映射到视图集的动作。

from django.urls import path from .views import ArticleViewSet article_list = ArticleViewSet.as_view({ 'get': 'list', 'post': 'create' }) article_detail = ArticleViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }) urlpatterns = [ path('articles/', article_list, name='article-list'), path('articles/<int:pk>/', article_detail, name='article-detail'), ]

可以看到,即使是一个简单的ModelViewSet,手动映射也稍显繁琐。当有多个ViewSet时,urls.py会迅速膨胀。

3.2 自动路由:SimpleRouter 与 DefaultRouter

DRF的Router类就是为了解决这个问题而生的。它能够自动为ViewSet注册标准的路由,并生成一个urlpatterns列表供Django的include使用。

SimpleRouter是最基础的路由器,为视图集生成标准的列表和详情路由。

# urls.py from rest_framework.routers import SimpleRouter from .views import ArticleViewSet # 1. 创建路由器实例 router = SimpleRouter() # 2. 注册视图集。第一个参数是URL前缀,第二个参数是视图集。 router.register(r'articles', ArticleViewSet, basename='article') # 3. 将路由器生成的路由包含到Django的urlpatterns中 urlpatterns = router.urls # 最终生成的URL模式等价于: # /articles/ -> ArticleViewSet.as_view({'get': 'list', 'post': 'create'}) # /articles/{pk}/ -> ArticleViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})

DefaultRouterSimpleRouter的增强版。除了标准路由,它还会额外生成一个API根视图(列出所有已注册的API端点)以及为每个端点生成一个.json格式的后缀(已不推荐使用)。

from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'articles', ArticleViewSet, basename='article') # 使用 DefaultRouter 后,访问根路径(如 /)会看到一个超链接的API列表,对调试非常友好。 urlpatterns = router.urls

@action装饰器生成的路由路由器同样能自动处理由@action装饰器定义的自定义端点。对于detail=True的动作,URL模式为/{prefix}/{lookup}/{url_path}/;对于detail=False的动作,URL模式为/{prefix}/{url_path}/

3.3 路由组合与命名空间

在大型项目中,你可能有多个应用(app),每个应用都有自己的views.py和路由。最佳实践是在每个应用的目录下创建自己的urls.py,并使用include将其整合到项目根路由中。

# 项目根 urls.py (myproject/urls.py) from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include('myapp.urls')), # 包含应用的路由 ] # 应用内部 urls.py (myapp/urls.py) from rest_framework.routers import DefaultRouter from . import views router = DefaultRouter() router.register(r'articles', views.ArticleViewSet, basename='article') router.register(r'categories', views.CategoryViewSet, basename='category') # 可以注册多个视图集 urlpatterns = router.urls

这样,你的API结构会非常清晰:/api/v1/articles//api/v1/categories/

关于basename参数basename用于URL反向解析时生成名称。如果你在视图集中定义了queryset属性,路由器通常可以自动推导出basename(通常是模型名的小写)。但如果你重写了get_queryset方法或者没有设置queryset则必须显式提供basename参数,否则会报错。提供一个明确的basename是一个好习惯。

router.register(r'published-articles', views.PublishedArticleViewSet, basename='published-article')

4. 核心配置与高级技巧

掌握了基本结构后,一些核心配置和高级技巧能让你的API更健壮、更易用。

4.1 认证、权限与限流

视图和视图集可以方便地配置全局或局部的认证、权限和限流策略。这些通常在settings.py中设置为全局默认值,也可以在具体的视图类中覆盖。

# settings.py 全局配置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', # 或 JWT ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # 默认要求登录 ], 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ], 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', # 匿名用户每天100次 'user': '1000/day' # 认证用户每天1000次 } } # 在视图中局部覆盖 from rest_framework.permissions import AllowAny, IsAdminUser class PublicArticleListView(generics.ListAPIView): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [AllowAny] # 这个视图允许任何人访问,覆盖全局设置 class ArticleAdminViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [IsAdminUser] # 仅管理员可访问 throttle_classes = [UserRateThrottle] # 应用用户限流

4.2 过滤、搜索与排序

对于列表接口,过滤、搜索和排序是刚需。DRF通过与django-filter等第三方库的深度集成,让这些功能变得非常简单。

首先,安装django-filterpip install django-filter

然后,在视图或视图集中配置filter_backendsfilterset_fields

from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import SearchFilter, OrderingFilter class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer # 配置过滤后端 filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] # 1. 精确过滤字段 filterset_fields = ['category', 'author', 'status'] # 2. 搜索字段(模糊匹配) search_fields = ['title', 'content', 'author__username'] # 3. 排序字段 ordering_fields = ['created_at', 'updated_at', 'like_count'] ordering = ['-created_at'] # 默认排序 # 使用示例: # GET /articles/?category=1&author=2 -> 精确过滤 # GET /articles/?search=django -> 在title, content, author__username中搜索“django” # GET /articles/?ordering=like_count -> 按点赞数升序 # GET /articles/?ordering=-created_at -> 按创建时间降序(默认)

实操心得:自定义复杂过滤filterset_fields适合简单的等值过滤。对于范围过滤(如时间区间、价格区间)或更复杂的逻辑,你需要定义一个自定义的FilterSet类。

import django_filters from .models import Article class ArticleFilter(django_filters.FilterSet): created_after = django_filters.DateFilter(field_name='created_at', lookup_expr='gte') created_before = django_filters.DateFilter(field_name='created_at', lookup_expr='lte') class Meta: model = Article fields = ['category', 'author', 'status'] class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = [DjangoFilterBackend] filterset_class = ArticleFilter # 使用自定义的FilterSet # 使用示例: # GET /articles/?created_after=2023-01-01&created_before=2023-12-31

4.3 分页

DRF内置了多种分页样式。在settings.py中设置全局分页,或在视图中单独设置。

# settings.py 全局配置 REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 } # 在视图中局部配置或自定义 from rest_framework.pagination import PageNumberPagination class LargeResultsSetPagination(PageNumberPagination): page_size = 50 page_size_query_param = 'page_size' # 允许客户端通过 `?page_size=100` 临时调整 max_page_size = 1000 # 允许的最大页面大小 class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer pagination_class = LargeResultsSetPagination # 使用自定义分页类

5. 常见问题与排查技巧实录

在实际开发中,你一定会遇到各种“坑”。这里记录了几个最常见的问题和解决方法。

5.1 视图集注册后,自定义的@action端点访问404

问题描述:你定义了一个@action(detail=True, methods=[‘post’])的方法like,但访问/articles/1/like/却返回404。

排查步骤:

  1. 检查detail参数:这是最常见的原因。如果你的操作是针对单个实例的(需要pk),detail必须设为True。如果针对集合,则设为False。设反了会导致路由不匹配。
  2. 检查路由器注册:确保你的视图集已经正确注册到了路由器(router.register)。
  3. 检查URL配置包含:确保router.urls已经被包含到了Django项目的urlpatterns中,并且路径前缀正确。
  4. 检查HTTP方法:确认你使用的HTTP方法(GET, POST等)与@action装饰器中methods参数定义的一致。
  5. 使用router.urls调试:在shell中打印router.urls,查看自动生成的所有URL模式,确认你的自定义端点是否在其中。
# 在Python shell中调试 from myapp.urls import router for url in router.urls: print(url.pattern, url.name)

5.2 查询集性能问题(N+1查询)

问题描述:列表接口返回大量数据时,响应速度极慢,数据库查询次数暴增。

问题根源:序列化器在序列化关联字段(如authorForeignKey)时,如果没有优化,会对每个对象单独发起查询,导致著名的“N+1查询问题”。

解决方案:使用select_relatedprefetch_related。 这两个是Django ORM的性能利器,必须在视图的get_queryset方法中使用。

  • select_related:用于“一对一”或“多对一”关系(ForeignKeyOneToOneField),通过SQL JOIN一次性获取关联对象。
  • prefetch_related:用于“多对多”或反向“一对多”关系(ManyToManyFieldreverse ForeignKey),通过额外的查询预取相关对象集,但仍在内存中高效关联。
class ArticleViewSet(viewsets.ModelViewSet): serializer_class = ArticleSerializer def get_queryset(self): # 优化查询:预取作者信息(ForeignKey)和标签(ManyToMany) return Article.objects.all() \ .select_related('author') \ # author 是 ForeignKey .prefetch_related('tags') # tags 是 ManyToMany

如何判断是否需要优化?使用Django Debug Toolbar或查看数据库查询日志。如果你发现一个列表请求产生了数十甚至上百条SQL语句,基本就是N+1问题。

5.3 权限校验失败,但错误信息不明确

问题描述:用户访问一个需要特定权限的接口,返回了403 Forbidden,但前端或用户不知道具体为什么被拒绝。

解决方案:自定义权限类并返回详细的错误信息。DRF内置的权限类错误信息比较通用。你可以创建自定义权限类,在has_permissionhas_object_permission方法返回False时,附带一个消息。

from rest_framework import permissions class IsArticleAuthorOrReadOnly(permissions.BasePermission): """ 自定义权限:只有文章的作者可以修改或删除,其他用户只读。 """ message = '您不是该文章的作者,无权进行此操作。' # 自定义错误信息 def has_object_permission(self, request, view, obj): # 安全方法(GET, HEAD, OPTIONS)总是允许 if request.method in permissions.SAFE_METHODS: return True # 写操作只允许作者本人 return obj.author == request.user # 在视图中使用 class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView): permission_classes = [IsArticleAuthorOrReadOnly] # ...

当权限校验失败时,DRF会使用你定义的message属性作为错误信息返回,对前端更加友好。

5.4 序列化器验证逻辑复杂,难以维护

问题描述:一个创建或更新接口的验证逻辑非常复杂,涉及多个字段的联动校验,写在序列化器的validate方法里导致代码臃肿。

解决方案:将复杂验证逻辑拆分为序列化器字段级别的validators或单独的验证函数。DRF的验证器是可重用的。

from rest_framework import serializers from django.utils import timezone def validate_publish_date(value): """自定义验证器:发布日期不能是过去的时间。""" if value < timezone.now().date(): raise serializers.ValidationError("发布日期不能是过去的时间。") return value class ArticleSerializer(serializers.ModelSerializer): publish_date = serializers.DateField(validators=[validate_publish_date]) class Meta: model = Article fields = '__all__' def validate(self, attrs): # 对象级别的复杂验证 # 例如:如果文章状态是“已发布”,则必须填写 publish_date if attrs.get('status') == 'published' and not attrs.get('publish_date'): raise serializers.ValidationError({ 'publish_date': '发布文章时必须指定发布日期。' }) return attrs

对于极其复杂的业务规则,甚至可以考虑将验证逻辑抽离到服务层(Service Layer)或表单(Form)中,在视图的perform_createperform_update方法中调用。

5.5 路由冲突与优先级问题

问题描述:自定义的@action路径与标准路由(如{pk}/)冲突,或者多个@action路径之间冲突。

根本原因:Django的URL解析是按照urlpatterns列表的顺序进行的,第一个匹配的规则生效。路由器生成的URL模式也有其内部顺序。

解决方案与避坑指南:

  1. 理解路由顺序:对于同一个视图集,路由器生成的URL模式顺序通常是:自定义@actiondetail=False) -> 列表路由(/) -> 自定义@actiondetail=True) -> 详情路由(/{pk}/)。但这并非绝对,取决于注册顺序和实现细节。
  2. 避免路径重叠:不要定义像@action(detail=True, url_path=’update’)这样的动作,因为它会与标准的update动作(对应PUT /{pk}/)冲突。使用更具业务语义的名字,如publishlike
  3. 谨慎使用通配符:在项目的根urls.py中使用include时,注意路径的包含关系。一个常见的错误是在项目根路径有一个贪婪匹配(如path(‘api/’, include(‘myapp.urls’))),然后又为其他静态文件或管理后台定义了路径,导致它们被api/路径“吃掉”。确保将更具体的路径放在前面,更通用的路径放在后面。
  4. 使用basename区分:如果你在不同的路由器或不同的include路径下注册了同名视图集,务必使用不同的basename,否则URL反向解析会出错。

我个人在实际项目中的体会是,视图和路由的设计是API的骨架。初期多花一点时间规划好视图集的划分、自定义动作的设计以及路由的层级结构,后期维护起来会轻松十倍。尤其是在团队协作中,一套清晰、一致的API约定能极大降低沟通成本。记住,DRF提供的各种工具不是为了炫技,而是为了让你写出更清晰、更健壮、更易维护的代码。从最简单的APIView开始,逐步过渡到ViewSet和路由器,根据项目实际复杂度选择合适的工具,这才是驾驭DRF视图与路由的正道。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 6:44:55

Octo Loop上线:让长任务跑出聊天框

单个 Agent、单次对话、几分钟跑完的任务&#xff0c;在聊天窗口里体验顺畅。但当任务拉长到数小时、需要多个执行者协作&#xff0c;或者人要中途离开再回来时&#xff0c;问题就出现了&#xff1a;上下文散在几百条消息里&#xff0c;哪个任务正在执行、哪个在等人、哪个已经…

作者头像 李华
网站建设 2026/8/6 6:42:18

iOS开发进阶:深入解析UINavigationController、UITabBarController与控制器通信

1. 项目概述&#xff1a;为什么ViewController是iOS开发的基石如果你刚接触iOS开发&#xff0c;可能会觉得UIButton、UILabel这些控件是构建界面的主角。但当你真正开始构建一个完整的应用时&#xff0c;很快就会意识到&#xff0c;真正在幕后掌控全局、串联起所有界面和业务逻…

作者头像 李华
网站建设 2026/8/6 6:42:08

西门子PLC PID控制实战:从CONT_C功能块到参数整定与工程调试

1. 项目概述&#xff1a;为什么PID是工业自动化的“定海神针”在工业控制领域&#xff0c;无论是调节一个恒温箱的温度&#xff0c;还是稳定一个储水罐的液位&#xff0c;亦或是控制一台电机的转速&#xff0c;我们最终追求的都是一个“稳定”的目标值。然而&#xff0c;现实世…

作者头像 李华
网站建设 2026/8/6 6:36:23

同一篇论文,DeepSeek 和 Kimi 谁画的流程图更能看?(提示词附上)

给你一段能直接抄走的提示词&#xff1a;把论文链接丢进去&#xff0c;AI 读完吐回一张流程图&#xff0c;还是能进 draw.io 继续改的那种。 读论文最卡的往往是不知道从哪块看起。要是先有张图把逻辑理出来——研究问题、方法怎么走、结论落在哪——你就知道该从哪下手。下面…

作者头像 李华
网站建设 2026/8/6 6:32:50

基于微信的远程自动化:WorkBuddy核心原理与实战部署指南

1. 从“手动”到“自动”&#xff1a;一个远程办公效率困境的破局思路你有没有过这样的经历&#xff1f;周末在家&#xff0c;突然想起公司电脑上有个文件没发&#xff0c;或者一个脚本需要运行一下。于是你不得不打开电脑&#xff0c;用各种远程桌面软件连回公司&#xff0c;输…

作者头像 李华
网站建设 2026/8/6 6:32:11

对象的消息模型

对象的消息模型 在面向对象编程&#xff08;OOP&#xff09;的广袤宇宙中&#xff0c;“对象”被视为程序的基本单元&#xff0c;而对象之间如何通信、协作&#xff0c;则是构建复杂系统的核心问题。这种通信机制&#xff0c;在经典理论中被称为“消息传递”&#xff08;Messag…

作者头像 李华