← 返回题目列表

Django REST Framework 中 APIView、GenericAPIView 和 ViewSet 有什么区别?

高频 中等 第 14 / 27 题 更新于 2026/07/31
DRFAPIViewViewSetGenericAPIView

简化版

DRF 的 APIView 最底层,适合手写请求方法;GenericAPIView 加入 queryset、serializer、pagination 等通用能力;ViewSet 把 list、retrieve、create、update、destroy 等动作组织在一起,常配合 router 自动生成路由。

详细版

APIView 类似 Django CBV 的 API 版,需要自己写 get/postGenericAPIView 提供 get_queryset()get_serializer() 等基础设施,再配合 mixins 快速实现 CRUD。ViewSet 不直接写 HTTP 方法,而是写动作方法。

class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

面试回答要强调:抽象越高,开发越快,但定制和理解成本也越高。简单特殊接口用 APIView,标准资源 CRUD 用 ViewSet/ModelViewSet。

完整版教学

一、DRF 为什么有多层视图抽象

API 开发既有标准 CRUD,也有很多特殊接口。如果只有最底层 APIView,重复代码很多;如果只有最高层 ViewSet,特殊逻辑又显得绕。DRF 提供多层抽象,让你按复杂度选择。

APIView -> GenericAPIView + Mixins -> ViewSet -> ModelViewSet

越往右,默认能力越多,约定也越多。

二、APIView 适合什么

APIView 直接写 HTTP 方法,控制最清晰。适合登录、回调、聚合接口、非标准资源动作。

class PingView(APIView):
    def get(self, request):
        return Response({"ok": True})

它不会自动知道 queryset 或 serializer,你要自己组织逻辑。优点是直观,缺点是重复。

三、GenericAPIView 和 Mixins

GenericAPIView 提供获取 queryset、serializer、object、pagination 的通用方法。Mixins 提供 list/create/retrieve/update/destroy 等动作。

class UserListView(ListModelMixin, GenericAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get(self, request):
        return self.list(request)

这种组合比 APIView 省代码,但比 ModelViewSet 更显式。

四、ViewSet 和 Router

ViewSet 把动作和 HTTP 方法映射交给 router。你写 listretrieve 等动作,router 自动生成 URL。

GET /users/ -> list
POST /users/ -> create
GET /users/1/ -> retrieve

这适合 REST 风格资源接口。团队规范统一时,ViewSet 能让 API 结构很整齐。

ViewSet 的关键不是少写几行路由,而是把资源动作按 REST 约定组织起来。

五、ModelViewSet 的边界

ModelViewSet 一次性提供 CRUD。它很快,但也容易让新手把所有字段、所有操作都暴露出去。生产中要认真配置权限、过滤、字段、queryset。

def get_queryset(self):
    return User.objects.filter(tenant=self.request.user.tenant)

如果忘记按租户或用户过滤,可能出现越权数据泄露。

六、怎么选择

场景推荐
特殊回调接口APIView
标准列表/详情但需显式控制GenericAPIView + Mixins
标准资源 CRUDViewSet / ModelViewSet
很复杂的业务动作APIView 或自定义 action

不要为了高级而高级。接口越特殊,越需要显式;接口越标准,越适合约定。

七、权限和序列化要放在哪里

DRF 的视图层负责组织请求流程,但权限、序列化、过滤和业务服务要分清。权限类判断能不能访问,serializer 负责输入输出转换,queryset 控制数据范围,复杂业务最好放 service 或 model 方法中。

permission -> queryset filter -> serializer -> service/domain logic

如果把所有逻辑都塞进 ViewSet,短期很快,长期会变成难测、难复用的大类。

八、面试里的选择表达

回答时可以先给原则,再给例子。比如:“标准资源 CRUD 用 ModelViewSet;需要精细控制但仍是模型资源,用 GenericAPIView + Mixins;登录回调、报表聚合这类非资源接口,用 APIView。”

标准资源: ViewSet
半标准资源: GenericAPIView
特殊接口: APIView

这样比只背类名区别更像工程决策。

九、常见误区与追问

  • 误区:ModelViewSet 是最佳实践,所有接口都用它。 标准 CRUD 适合,特殊业务可能 APIView 更清晰。
  • 误区:ViewSet 自动保证权限安全。 权限和 queryset 过滤仍要自己配置。
  • 误区:APIView 不能用 serializer。 当然可以,只是要自己调用。
  • 追问:router 做什么? 根据 ViewSet action 生成 URL 映射。
  • 追问:@action 有什么用? 给 ViewSet 增加自定义动作。
  • 追问:get_queryset 为什么常重写? 按当前用户、租户、权限动态过滤数据。
  • 追问:GenericAPIView 比 APIView 多什么? queryset、serializer、object、pagination 等通用钩子。

十、加强记忆

DRF 视图抽象记成“手写、半自动、全资源”。APIView 最清楚,GenericAPIView 加通用能力,ViewSet 按资源动作组织,ModelViewSet 快速 CRUD。选择时看接口是否标准,别让高层抽象掩盖权限和数据过滤。