DRF视图与路由进阶:从基础CRUD到复杂API架构设计
2026/8/6 5:49:57 网站建设 项目流程

1. 项目概述:从“能用”到“优雅”的DRF视图与路由实践

如果你已经用Django REST framework(DRF)搭过几个简单的API接口,体验过它用几行代码就搞定序列化和基础CRUD的爽快感,那么接下来,你很可能正站在一个关键的十字路口。很多开发者,包括几年前的我自己,在初步上手DRF后,会陷入一个“能用但别扭”的境地:视图代码开始变得冗长,if request.method == 'GET'if request.method == 'POST'堆满了视图函数;路由配置虽然简单,但一旦涉及到嵌套资源、自定义动作或者复杂的权限校验,urls.py文件就变得难以维护。这感觉就像你拿到了一把精良的瑞士军刀,却只用来拧螺丝,完全没发挥出它那些专业工具模块的威力。

今天,我们就来深挖DRF视图和路由这两个核心组件,目标不仅仅是“实现功能”,而是如何“优雅地组织”。我们将超越官方文档的简单示例,聚焦于实际项目中高频出现的复杂场景,比如如何为同一个模型设计出满足不同用户角色(如普通用户和管理员)的精细化视图集,如何清晰、安全地配置路由以暴露恰当的API端点,以及如何避免那些让代码后期难以扩展的常见陷阱。无论你是正在重构一个陈旧的DRF项目,还是希望从一开始就为你的新应用打下坚实、可维护的基础,接下来的内容都将提供一套经过实战检验的思路和具体方案。

2. DRF视图层深度解析:超越APIViewGenericAPIView

DRF的视图类提供了从底层到高层的多种抽象,理解它们的继承关系和适用场景,是写出高效、清晰代码的第一步。很多教程止步于APIViewModelViewSet,但这中间广阔的天地,才是灵活性的所在。

2.1 视图类继承树与核心职责剖析

DRF的视图类可以看作一个金字塔:

  1. django.views.generic.View:这是Django的类视图基类。DRF的APIView继承了它,并彻底改变了请求/响应的处理方式。在标准Django视图中,你拿到的是HttpRequest和返回HttpResponse;而在APIView中,你拿到的是DRF封装过的Request对象,并且期望你返回Response对象。这个Request对象提供了.data属性(解析后的请求体)、.query_params属性(等同于request.GET但命名更清晰),以及.user,.auth等由认证模块填充的属性。Response对象则能自动根据客户端接受的 Content-Type 来渲染数据。

  2. rest_framework.views.APIView:这是DRF所有视图的基石。它最核心的功能是:

    • 调度 (dispatch):在调用你写的get,post等方法前,它会依次运行认证、权限、限流等组件。
    • 异常处理:将Django的Http404PermissionDenied以及DRF自身的APIException等异常,转化为结构化的错误响应。
    • 渲染与解析:根据请求头,选择合适的解析器来解析请求体,并选择合适的渲染器来渲染响应数据。

    当你需要实现一个与标准模型CRUD完全不同的、逻辑复杂的端点时,直接继承APIView是最直接的选择。例如,一个处理文件上传、调用外部服务进行异步处理,然后返回任务ID的接口。

  3. rest_framework.generics.GenericAPIView:这是引入“通用”逻辑的关键一层。它提供了与数据库模型交互的骨架,但不包含任何具体的HTTP方法处理逻辑。它的核心属性包括:

    • queryset:定义这个视图用于列表和检索操作的基础查询集。
    • serializer_class:定义用于验证输入和序列化输出的序列化器类。
    • lookup_fieldlookup_url_kwarg:定义如何从URL中获取单个对象的标识符(默认是pk)。
    • get_queryset(),get_object(),get_serializer()等方法:提供了可重写的钩子,让你能基于请求动态改变查询集、对象获取逻辑或序列化器。

    GenericAPIView本身没有getpost方法。它等待mixin来赋予它生命。

  4. rest_framework.generics.*&rest_framework.mixins.*:这是DRF“乐高积木”哲学的核心。Mixins(混入类)提供了单一、专注的行为:

    • ListModelMixin:提供.list(request, *args, **kwargs)方法,用于返回查询集列表。
    • CreateModelMixin:提供.create(request, *args, **kwargs)方法,用于创建新对象。
    • RetrieveModelMixin:提供.retrieve(request, *args, **kwargs)方法,用于返回单个对象详情。
    • UpdateModelMixin:提供.update(全更新)和.partial_update(部分更新)方法。
    • DestroyModelMixin:提供.destroy(request, *args, **kwargs)方法,用于删除对象。

    generics下的类,如ListAPIView,CreateAPIView,RetrieveUpdateDestroyAPIView,则是GenericAPIView与一个或多个Mixins的预组合。例如,ListAPIView = GenericAPIView + ListModelMixin

  5. rest_framework.viewsets.ViewSetModelViewSet:视图集(ViewSet)将一组相关的视图逻辑组织在一起。ViewSet类本身不提供任何动作,它更像是一个基于动作(action)的APIView容器。ModelViewSet则继承了GenericAPIView,并一次性混入了所有的5个ModelMixin,同时提供了默认的list,create,retrieve,update,partial_update,destroy方法。它是快速构建标准模型RESTful接口的终极利器。

核心心得:不要一上来就用ModelViewSet。先问自己:这个端点需要标准CRUD的所有操作吗?如果只需要“列表”和“创建”,那么generics.ListCreateAPIView更合适,它更精简,暴露的接口也更少,更符合最小接口原则。ModelViewSet功能强大,但也可能无意中暴露你不想提供的操作(比如destroy),需要通过路由或权限仔细控制。

2.2 动态序列化器与查询集:应对复杂业务场景

在实际项目中,一个视图处理多种场景是常态。例如,用户列表接口返回简略信息,而用户详情接口返回全部信息;或者普通用户只能看到自己的文章,而管理员能看到所有人的文章。硬编码querysetserializer_class会使得代码僵化。

1. 动态序列化器通过在视图中重写get_serializer_class()方法,可以根据请求的不同,返回不同的序列化器。

from rest_framework import generics from .models import User from .serializers import UserListSerializer, UserDetailSerializer, UserCreateSerializer class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() def get_serializer_class(self): # 根据不同的动作(action)选择序列化器 if self.action == 'list': return UserListSerializer # 仅包含 id, username, email elif self.action == 'retrieve': return UserDetailSerializer # 包含所有字段,甚至关联的profile elif self.action == 'create': return UserCreateSerializer # 包含密码等创建时必需的字段 # 对于 update, partial_update, 可以复用 Detail 或创建一个专门的 UpdateSerializer return super().get_serializer_class()

这里的关键是self.action属性,它在视图集被路由映射后,会被自动设置为当前请求对应的动作名,如list,create,retrieve,update,partial_update,destroy,或者你自定义的动作。

2. 动态查询集权限控制经常需要在数据层面进行过滤。重写get_queryset()方法是标准做法。

from rest_framework import permissions, viewsets class ArticleViewSet(viewsets.ModelViewSet): serializer_class = ArticleSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): # 确保基础查询集包含所有可能需要的数据,避免N+1查询 queryset = Article.objects.select_related('author').prefetch_related('tags') # 根据用户角色进行过滤 user = self.request.user if not user.is_staff: # 非管理员只能看到已发布(published)的文章 queryset = queryset.filter(status='published') # 可以根据URL参数或其他请求信息进一步过滤 category = self.request.query_params.get('category', None) if category: queryset = queryset.filter(category__slug=category) return queryset

重要提示:在get_queryset()中,self.request是可用的。但请注意,在类属性定义阶段(如queryset = Article.objects.all())是无法访问request的。这也是为什么推荐在视图类中直接将queryset定义为None,并完全通过get_queryset()方法来提供查询集,这样可以获得最大的灵活性。

2.3 自定义动作(Custom Actions):扩展你的API语义

ModelViewSet提供了标准的CRUD操作,但业务需求远不止这些。比如,给一篇文章“点赞”,或者“激活”一个用户账户。这些操作不适合直接映射到update(更新整个资源),这时就需要自定义动作。

使用@action装饰器可以轻松实现:

from rest_framework.decorators import action from rest_framework.response import Response class ArticleViewSet(viewsets.ModelViewSet): # ... 其他代码 ... @action(detail=True, methods=['post']) def like(self, request, pk=None): """为指定文章点赞""" article = self.get_object() user = request.user if article.likes.filter(id=user.id).exists(): article.likes.remove(user) liked = False else: article.likes.add(user) liked = True article.save() return Response({'status': 'success', 'liked': liked, 'total_likes': article.likes.count()}) @action(detail=False, methods=['get']) def recent(self, request): """获取最近发布的10篇文章""" recent_articles = self.get_queryset().order_by('-published_at')[:10] serializer = self.get_serializer(recent_articles, many=True) return Response(serializer.data)
  • detail=True:表示这个动作是针对单个对象(如/articles/1/like/)。此时,self.get_object()可用。
  • detail=False:表示这个动作是针对整个集合(如/articles/recent/)。此时,操作的是self.get_queryset()
  • methods:指定允许的HTTP方法列表。
  • url_pathurl_name:可以自定义生成的URL路径和名称。

自定义动作极大地丰富了API的表达能力,使其更贴近业务语言,而不仅仅是数据库的增删改查。

3. 路由配置的艺术:清晰、安全与可维护性

DRF提供了SimpleRouterDefaultRouter来自动生成视图集的路由。但自动生成并不意味着我们可以不假思索。路由配置是API设计意图的直观体现,也关乎着API的安全边界。

3.1 Router 的工作原理与选择

  • SimpleRouter:生成标准的 list, create, retrieve, update, partial_update, destroy 路由。它简单、清晰,是大多数情况下的首选。
  • DefaultRouter:在SimpleRouter的基础上,额外添加了一个默认的 API 根视图(列出所有注册的视图集的根路径),并且为可浏览的API(JSONRenderer)提供了格式后缀(如.json)。

注册方式非常简单:

from rest_framework.routers import DefaultRouter from .views import ArticleViewSet, UserViewSet router = DefaultRouter() router.register(r'articles', ArticleViewSet, basename='article') router.register(r'users', UserViewSet, basename='user') urlpatterns = router.urls

这将会自动生成如下路由(以articles为例):

  • /articles/- GET: 列表, POST: 创建
  • /articles/{pk}/- GET: 详情, PUT: 全更新, PATCH: 部分更新, DELETE: 删除

踩坑记录basename参数非常重要。当你的视图集没有设置queryset属性,或者你想覆盖默认的basename时,必须显式提供。否则,DRF在反向解析URL(reverse)或生成 schema 时可能会报错。一个简单的规则是:只要你重写了视图集的get_queryset()方法,就最好显式地设置basename

3.2 精细化控制:暴露你想要的,隐藏你不该暴露的

自动生成所有路由很方便,但有时是危险的。例如,你的UserViewSet可能继承自ModelViewSet,但你不想让任何人通过API直接删除用户。有几种方法可以控制:

方法一:在视图集中禁用特定方法最简单的方式是在视图集中不提供该方法。

class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer # 直接覆盖 destroy 方法,使其返回 405 Method Not Allowed def destroy(self, request, *args, **kwargs): return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

或者,更优雅地,使用http_method_names属性(继承自Django的View):

class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer http_method_names = ['get', 'post', 'put', 'patch', 'head', 'options'] # 移除 'delete'

方法二:在路由注册时排除或额外添加DRF的Router允许你在注册时通过routes参数进行深度定制,但这相对复杂。更常见的做法是结合使用@action装饰器和在urls.py中手动添加额外路由。

方法三:使用多个视图集这是我最推荐的做法,它体现了“单一职责”原则。为同一个模型创建不同的视图集,用于不同的场景。

# views.py class UserPublicViewSet(viewsets.ReadOnlyModelViewSet): """公开API,只读用户信息""" queryset = User.objects.filter(is_active=True) serializer_class = UserPublicSerializer # 无需认证即可访问 class UserAdminViewSet(viewsets.ModelViewSet): """管理员API,操作用户""" queryset = User.objects.all() serializer_class = UserAdminSerializer permission_classes = [permissions.IsAdminUser] # 包含所有CRUD操作 # urls.py router = DefaultRouter() router.register(r'public/users', UserPublicViewSet, basename='public-user') router.register(r'admin/users', UserAdminViewSet, basename='admin-user')

这样,API的意图非常清晰:/public/users/是给所有人看的,只读;/admin/users/是管理后台,需要管理员权限。安全性、可维护性和清晰度都得到了提升。

3.3 嵌套路由与自定义路由映射

对于有关联关系的资源,如“文章”下的“评论”,我们常常需要嵌套路由:/articles/1/comments/。DRF的默认Router不直接支持嵌套,但我们可以通过多种方式实现。

方式一:使用@action装饰器这是实现“逻辑嵌套”最快捷的方式,在父资源的视图集上定义一个自定义动作。

class ArticleViewSet(viewsets.ModelViewSet): # ... @action(detail=True, methods=['get', 'post']) def comments(self, request, pk=None): """获取或创建某篇文章的评论""" article = self.get_object() if request.method == 'GET': comments = article.comments.all() serializer = CommentSerializer(comments, many=True) return Response(serializer.data) elif request.method == 'POST': # ... 创建评论的逻辑 ...

这种方式生成的URL是/articles/{pk}/comments/,非常直观。但它将评论的列表和创建逻辑耦合在了文章视图集里,如果评论的操作很复杂,会使文章视图集变得臃肿。

方式二:手动配置嵌套路由(推荐用于复杂场景)在项目的urls.py中手动构建嵌套路由,给予你最大的控制权。

from django.urls import path, include from rest_framework_nested import routers # 推荐使用第三方库 drf-nested-routers from .views import ArticleViewSet, CommentViewSet router = routers.DefaultRouter() router.register(r'articles', ArticleViewSet, basename='article') # 创建嵌套路由器 articles_router = routers.NestedDefaultRouter(router, r'articles', lookup='article') articles_router.register(r'comments', CommentViewSet, basename='article-comments') urlpatterns = [ path('api/', include(router.urls)), path('api/', include(articles_router.urls)), ]

使用drf-nested-routers库后,CommentViewSet可以独立存在,并且能通过self.kwargs['article_pk']获取到父资源的主键,从而在get_queryset()中进行过滤:

class CommentViewSet(viewsets.ModelViewSet): serializer_class = CommentSerializer def get_queryset(self): # 通过嵌套路由传入的 article_pk 进行过滤 return Comment.objects.filter(article_id=self.kwargs['article_pk'])

这种方式分离了关注点,CommentViewSet可以拥有自己完整的CRUD逻辑,代码结构更清晰,特别适合子资源本身也有复杂操作的情况。

4. 视图与路由的进阶实战模式

掌握了基础组件后,我们可以将它们组合起来,解决更实际的工程问题。

4.1 模式一:基于用户角色的多视图集分发

对于一个博客系统,我们可能有以下角色和需求:

  • 匿名用户:可以浏览已发布的文章列表和详情。
  • 认证用户:可以创建草稿、评论、管理自己的文章。
  • 编辑:可以编辑所有文章的状态(如提交审核、发布)。
  • 管理员:可以管理所有文章、用户、分类等。

为每个角色创建独立的视图集是清晰的做法,但如何优雅地组织路由?我推荐使用Django的URL命名空间和条件路由。

# api/urls.py from django.urls import path, include from .public import views as public_views from .author import views as author_views from .editor import views as editor_views public_patterns = ([ path('articles/', public_views.ArticleListView.as_view()), path('articles/<int:pk>/', public_views.ArticleDetailView.as_view()), ], 'public') author_patterns = ([ path('articles/', author_views.ArticleViewSet.as_view({'get': 'list', 'post': 'create'})), path('articles/<int:pk>/', author_views.ArticleViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})), path('articles/<int:pk>/publish/', author_views.ArticleViewSet.as_view({'post': 'publish'})), ], 'author') editor_patterns = ([ path('articles/<int:pk>/review/', editor_views.review_article), ], 'editor') urlpatterns = [ path('v1/public/', include(public_patterns)), path('v1/author/', include(author_patterns)), path('v1/editor/', include(editor_patterns)), ]

然后,在前端或API网关,根据用户的身份(通过JWT Token中的角色声明判断)将请求代理到不同的路径前缀下。这种模式将权限控制前置到了路由层级,后端每个视图集只需关心自己角色范围内的业务逻辑,非常清晰。

4.2 模式二:组合视图(ViewSet)与通用视图(GenericAPIView)

不是所有端点都适合用ViewSet。对于特别简单或特别复杂的单一端点,使用generics.*APIViewAPIView可能更合适。一个项目里混合使用多种视图类型是完全正常的。

例如,一个统计仪表盘接口,需要聚合多个模型的数据:

from rest_framework.views import APIView from rest_framework.response import Response from django.db.models import Count, Q from datetime import datetime, timedelta class DashboardStatsView(APIView): permission_classes = [permissions.IsAdminUser] def get(self, request): today = datetime.now().date() last_week = today - timedelta(days=7) user_stats = { 'total': User.objects.count(), 'new_this_week': User.objects.filter(date_joined__gte=last_week).count(), 'active': User.objects.filter(last_login__gte=last_week).count(), } article_stats = { 'total': Article.objects.count(), 'published': Article.objects.filter(status='published').count(), 'drafts': Article.objects.filter(status='draft').count(), } # ... 更复杂的聚合查询 return Response({ 'users': user_stats, 'articles': article_stats, 'generated_at': datetime.now().isoformat(), })

这个视图不适合用ViewSet来套用,直接用APIView更加直白和灵活。

4.3 模式三:利用get_serializer_context传递额外数据到序列化器

序列化器有时需要访问当前请求(request)或视图(view)中的信息。例如,在序列化文章时,需要知道当前用户是否已经点赞过,以便在返回数据中添加一个is_liked_by_me字段。

这可以通过重写视图的get_serializer_context方法实现:

class ArticleViewSet(viewsets.ModelViewSet): def get_serializer_context(self): # 获取默认的上下文(包含 request, view, format) context = super().get_serializer_context() # 添加上下文信息 context['current_user'] = self.request.user # 你可以在这里进行一些预计算,避免在序列化器中重复查询 if self.request.user.is_authenticated: liked_article_ids = set(self.request.user.liked_articles.values_list('id', flat=True)) context['liked_article_ids'] = liked_article_ids return context # 在序列化器中 class ArticleSerializer(serializers.ModelSerializer): is_liked_by_me = serializers.SerializerMethodField() class Meta: model = Article fields = ['id', 'title', 'content', 'is_liked_by_me', ...] def get_is_liked_by_me(self, obj): # 从上下文中获取预计算好的集合 liked_article_ids = self.context.get('liked_article_ids', set()) return obj.id in liked_article_ids

这种方法避免了在序列化每个对象时都去数据库查询关联关系(即N+1查询问题),通过一次批量查询和上下文传递,极大地提升了性能。

5. 常见问题、性能陷阱与排查技巧

即使理解了原理,在实际开发中依然会遇到各种问题。下面是一些高频问题的排查思路和解决方案。

5.1 N+1 查询问题:性能的隐形杀手

这是使用ORM时最常见也最影响性能的问题。当你通过外键或ManyToMany字段访问关联对象时,如果处理不当,会导致大量额外的数据库查询。

问题现象:一个获取文章列表的接口,每篇文章需要显示作者名。如果序列化器里直接author.username,且没有做优化,那么对于N篇文章,会产生1次查询文章列表 + N次查询作者信息,共N+1次查询。

解决方案

  1. 使用select_relatedprefetch_related:在视图的get_queryset()中主动进行查询优化。
    def get_queryset(self): # select_related 用于 ForeignKey 和 OneToOneField (JOIN查询) # prefetch_related 用于 ManyToManyField 和 反向ForeignKey (额外查询,然后在Python中拼接) return Article.objects.select_related('author').prefetch_related('tags', 'comments').all()
  2. 在序列化器中使用Prefetch对象进行更精细的控制:有时prefetch_related预取的数据量过大,你可以使用Prefetch对象来定制预取的查询集。
    from django.db.models import Prefetch def get_queryset(self): recent_comments = Comment.objects.filter(created_at__gte=timezone.now()-timedelta(days=1)) return Article.objects.prefetch_related( Prefetch('comments', queryset=recent_comments, to_attr='recent_comment_list') ).all()
  3. 使用django-debug-toolbar监控查询:这是本地开发的必备工具。它能清晰展示每个页面/API请求执行的所有SQL查询、耗时,并高亮提示潜在的N+1问题。

5.2 权限校验与get_queryset()的协作

权限类(permission_classes)和get_queryset()都用于控制数据访问,但职责不同:

  • 权限类:决定当前请求的用户是否有权限执行当前请求的动作(如view,add,change,delete)。它返回TrueFalse,如果失败,DRF会直接返回403401响应,不会执行到get_queryset()
  • get_queryset():在权限检查通过后,决定返回哪些数据对象给后续的序列化或操作。它用于行级的数据过滤。

一个常见的误区是试图在get_queryset()中做所有的权限判断。正确的做法是:

  • 使用权限类进行粗粒度的访问控制(例如,IsAuthenticated要求登录,IsAdminUser要求管理员)。
  • get_queryset()中进行细粒度的数据过滤(例如,普通用户只能看到自己的数据)。
  • 对于更复杂的对象级权限(例如,用户只能修改自己创建的文章,但可以查看所有已发布的文章),可以结合使用DjangoModelPermissions或自定义权限类,并在get_object()方法中进行最终检查。

5.3 分页、过滤与排序的集成

对于列表接口,分页、过滤和排序是刚需。DRF提供了强大的支持。

  1. 分页:在settings.py中设置全局分页类,或在视图类中通过pagination_class属性指定。常用的有PageNumberPagination?page=2)和LimitOffsetPagination?limit=10&offset=20)。自定义分页类可以让你控制返回的字段格式。

  2. 过滤:强烈推荐使用django-filter库。安装后,在视图类中设置filter_backendsfilterset_class

    from django_filters.rest_framework import DjangoFilterBackend, FilterSet from rest_framework import viewsets class ArticleFilter(FilterSet): created_after = filters.DateFilter(field_name='created_at', lookup_expr='gte') author__username = filters.CharFilter(lookup_expr='icontains') class Meta: model = Article fields = ['status', 'category', 'tags'] class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = [DjangoFilterBackend] filterset_class = ArticleFilter

    这样,你就可以通过/articles/?status=published&created_after=2023-01-01&author__username=john进行复杂过滤。

  3. 排序:使用OrderingFilter

    from rest_framework.filters import OrderingFilter class ArticleViewSet(viewsets.ModelViewSet): filter_backends = [DjangoFilterBackend, OrderingFilter] ordering_fields = ['created_at', 'title', 'view_count'] ordering = ['-created_at'] # 默认排序

    客户端可以通过?ordering=view_count?ordering=-created_at,title进行排序。

5.4 自定义异常处理与响应格式统一

DRF有默认的异常处理,但你可能希望统一所有错误响应的格式,或者记录特定的异常。可以创建一个自定义的异常处理函数。

# settings.py REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.utils.custom_exception_handler', } # utils.py from rest_framework.views import exception_handler from rest_framework.response import Response from rest_framework import status import logging logger = logging.getLogger(__name__) def custom_exception_handler(exc, context): # 调用DRF默认的异常处理,获得标准错误响应 response = exception_handler(exc, context) if response is not None: # 自定义响应格式 custom_data = { 'success': False, 'code': response.status_code, 'message': response.data.get('detail', 'An error occurred.') if isinstance(response.data, dict) else str(response.data), 'data': None, # 在开发环境下,可以返回更详细的错误信息 # 'detail': response.data if settings.DEBUG else None } response.data = custom_data else: # 处理DRF未捕获的异常(通常是服务器内部错误500) logger.error(f"Unhandled exception: {exc}", exc_info=True) custom_data = { 'success': False, 'code': status.HTTP_500_INTERNAL_SERVER_ERROR, 'message': 'Internal server error.', 'data': None, } response = Response(custom_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return response

这样,无论API抛出什么异常,前端收到的错误格式都是一致的,便于处理。

视图和路由是DRF的骨架与脉络。花时间设计好它们,意味着为整个API项目奠定了清晰、健壮和可扩展的基础。记住,没有最好的模式,只有最适合你当前业务复杂度的模式。从简单的GenericAPIView开始,随着需求增长,逐步引入ViewSet、自定义动作、嵌套路由和精细化的权限控制,让代码的演进与业务的成长同步,这才是可持续的开发之道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询