DRF讲解
1. 什么是 Django 和 Django REST Framework?
在深入 ModelViewSet 之前,我们先简单了解一下背景知识:
- Django 是一个基于 Python 的 Web 开发框架,旨在帮助开发者快速构建安全、可扩展的 Web 应用。它遵循"不要重复自己"(DRY)和"快速开发"的原则,提供了许多内置功能,比如 ORM(对象关系映射)、用户认证、模板引擎等。
- Django REST Framework (DRF) 是 Django 的扩展库,专门用于构建 RESTful API。它让开发者可以轻松地将 Django 模型转换为 API 端点,供前端或其他服务调用。
- ViewSet 是 DRF 提供的一种高级视图类,
ModelViewSet是其中最常用的子类,用于处理模型的 CRUD(创建、读取、更新、删除)操作。
2. 什么是 ModelViewSet?
ModelViewSet 是 Django REST Framework 中的一个类,位于 rest_framework.viewsets 模块。它是一个高度抽象的工具,集成了处理模型数据的所有常见操作(CRUD),让你只需编写很少的代码,就能实现完整的 API 功能。
核心特点:
- 自动生成 CRUD 端点 :
ModelViewSet自动为你的模型提供以下 HTTP 方法对应的操作:GET:列表(list)和详情(retrieve)POST:创建(create)PUT/PATCH:更新(update/partial_update)DELETE:删除(destroy)
- 代码简洁 :通过继承
ModelViewSet,你只需指定模型和序列化器(Serializer),就能自动生成这些功能。 - 灵活性:可以自定义行为,比如添加权限、过滤、排序等。
3. 使用 ModelViewSet 的步骤
创建一个简单的"任务管理"应用,包含任务(Task)的增删改查功能。
3.1 准备工作
环境要求
-
已安装 Python 3(推荐 3.8 或更高版本)
-
已安装 Django 和 Django REST Framework:
bashpip install django djangorestframework
创建 Django 项目
-
创建一个新的 Django 项目:
bashdjango-admin startproject task_manager cd task_manager -
创建一个 Django 应用:
bashpython manage.py startapp tasks -
将
tasks应用和rest_framework添加到task_manager/settings.py的INSTALLED_APPS中:pythonINSTALLED_APPS = [ ... 'rest_framework', 'tasks', ]
3.2 定义模型
在 tasks/models.py 中定义一个简单的 Task 模型:
python
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
- 解释 :
title:任务的标题,最大长度 200 个字符。description:任务描述,允许为空。completed:是否完成,默认为False。created_at:创建时间,自动设置为当前时间。__str__:返回任务的字符串表示,便于调试。
运行迁移命令以创建数据库表:
bash
python manage.py makemigrations
python manage.py migrate
3.3 创建序列化器(Serializer)
序列化器负责将模型数据转换为 JSON 格式(或从 JSON 转换为模型)。在 tasks/serializers.py 中创建:
python
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'completed', 'created_at']
- 解释 :
ModelSerializer是 DRF 提供的类,自动根据模型生成序列化逻辑。Meta类指定关联的模型(Task)和需要序列化的字段。fields列出要包含的字段,id是 Django 模型自动生成的。
3.4 创建 ModelViewSet
在 tasks/views.py 中定义 ModelViewSet:
python
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
- 解释 :
queryset:指定视图使用的查询集,这里是所有Task对象。serializer_class:指定序列化器,用于处理输入输出数据。ModelViewSet自动提供了list、retrieve、create、update、partial_update和destroy方法。
3.5 配置路由
为了让 API 可访问,需要将 TaskViewSet 注册到路由中。在 task_manager/urls.py 中:
python
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from tasks.views import TaskViewSet
# 创建路由器并注册 ViewSet
router = DefaultRouter()
router.register(r'tasks', TaskViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
]
- 解释 :
DefaultRouter是 DRF 提供的工具,自动为ModelViewSet生成 URL 路由。router.register注册TaskViewSet,并将 URL 前缀设为tasks。include(router.urls)将路由纳入主 URL 配置,API 端点将以/api/tasks/开头。
3.6 测试 API
-
启动 Django 开发服务器:
bashpython manage.py runserver -
访问
http://127.0.0.1:8000/api/tasks/,你会看到 DRF 提供的交互式 API 界面。 -
测试以下功能:
- GET
/api/tasks/:列出所有任务。 - POST
/api/tasks/:创建新任务(通过 JSON 提交{"title": "新任务", "description": "描述", "completed": false})。 - GET
/api/tasks/1/:获取 ID 为 1 的任务详情。 - PUT
/api/tasks/1/:更新 ID 为 1 的任务。 - DELETE
/api/tasks/1/:删除 ID 为 1 的任务。
- GET
4. 深入理解 ModelViewSet
4.1 ModelViewSet 的默认行为
ModelViewSet 继承了多个 Mixin 类,提供了以下方法:
| 方法 | HTTP 方法 | 功能 | URL 示例 |
|---|---|---|---|
list |
GET | 返回所有对象的列表 | /api/tasks/ |
retrieve |
GET | 返回单个对象的详情 | /api/tasks/1/ |
create |
POST | 创建新对象 | /api/tasks/ |
update |
PUT | 更新现有对象(整体更新) | /api/tasks/1/ |
partial_update |
PATCH | 部分更新对象 | /api/tasks/1/ |
destroy |
DELETE | 删除对象 | /api/tasks/1/ |
这些方法由 ModelViewSet 自动实现,你无需手动编写。
4.2 自定义 ModelViewSet
虽然 ModelViewSet 提供了默认实现,但你可以通过重写方法或添加属性来自定义行为。以下是一些常见自定义场景:
示例 1:添加权限控制
限制只有登录用户可以访问 API。在 tasks/views.py 中:
python
from rest_framework import viewsets, permissions
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
permission_classes = [permissions.IsAuthenticated] # 要求登录
-
解释 :
permission_classes指定只有通过认证的用户才能访问 API。 -
需要在
settings.py中启用认证:pythonREST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], }
示例 2:过滤查询集
只显示当前用户的任务:
python
class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return Task.objects.filter(owner=self.request.user)
-
解释 :重写
get_queryset方法,根据当前用户过滤任务。 -
需要在
Task模型中添加owner字段:pythonfrom django.contrib.auth.models import User class Task(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) ...
示例 3:自定义序列化器行为
假设你想在 list 操作时只返回部分字段:
python
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
def get_serializer_class(self):
if self.action == 'list':
return TaskListSerializer # 自定义序列化器
return TaskSerializer
class TaskListSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title'] # 仅返回 id 和 title
- 解释 :
get_serializer_class允许根据请求类型(action)动态选择序列化器。
6. 常见问题解答
Q1:为什么使用 ModelViewSet 而不是普通视图?
A:ModelViewSet 提供了开箱即用的 CRUD 功能,减少重复代码。对于快速开发 API 或原型,它非常高效。如果需要更多自定义,可以使用普通的 APIView 或 GenericAPIView。
Q2:如何处理复杂的 API 逻辑?
A:可以重写 ModelViewSet 的方法(如 create、update)或使用自定义动作(@action 装饰器)。
Q3:如何学习更多高级功能?
A:学习 DRF 的权限、过滤、分页和认证系统。尝试实现 token 认证(使用 rest_framework.authtoken 或 JWT)。

面试题
简单题目 (2 道)
1. 什么是 ModelViewSet,它在 Django REST Framework 中有什么作用?
问题描述 :请简要说明 ModelViewSet 的定义以及它在构建 API 时的主要功能。
答案提示:
ModelViewSet是 Django REST Framework 提供的一个高级视图类,继承自GenericViewSet和多个 Mixin(如CreateModelMixin、ListModelMixin等)。- 它自动为模型提供 CRUD 操作(创建、读取、更新、删除)的 API 端点。
- 通过指定
queryset和serializer_class,可以快速生成 RESTful API。 - 示例:
class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all(); serializer_class = TaskSerializer
考察点 :理解 ModelViewSet 的基本概念和用途。
2. 如何为 ModelViewSet 配置路由?
问题描述 :说明如何使用 Django REST Framework 的路由器(Router)为 ModelViewSet 配置 URL 路由,并提供代码示例。
答案提示:
-
使用
rest_framework.routers.DefaultRouter创建路由器。 -
调用
router.register方法注册ModelViewSet,指定 URL 前缀和视图集。 -
示例:
pythonfrom rest_framework.routers import DefaultRouter from .views import TaskViewSet router = DefaultRouter() router.register(r'tasks', TaskViewSet) urlpatterns = [ path('api/', include(router.urls)), ] -
自动生成
/api/tasks/(列表和创建)和/api/tasks/<id>/(详情、更新、删除)等端点。
考察点 :掌握 ModelViewSet 与路由器的集成。
中等题目 (5 道)
3. 如何在 ModelViewSet 中限制查询集只返回当前用户的数据?
问题描述 :假设有一个 Task 模型,包含 owner 字段(关联到 User 模型)。如何修改 TaskViewSet,使 GET /api/tasks/ 只返回当前登录用户的任务?
答案提示:
-
重写
get_queryset方法,根据self.request.user过滤查询集。 -
示例:
pythonclass TaskViewSet(viewsets.ModelViewSet): serializer_class = TaskSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return Task.objects.filter(owner=self.request.user) -
需要确保用户已登录(
IsAuthenticated权限)。
考察点 :理解 get_queryset 的动态过滤和权限控制。
4. 如何在 ModelViewSet 中为不同操作使用不同的序列化器?
问题描述 :在 TaskViewSet 中,list 操作只需要返回 id 和 title,而其他操作需要返回所有字段。如何实现?
答案提示:
-
重写
get_serializer_class方法,根据self.action返回不同的序列化器。 -
示例:
pythonclass TaskListSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ['id', 'title'] class TaskSerializer(serializers.ModelSerializer): class Meta: model = Task fields = '__all__' class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() def get_serializer_class(self): if self.action == 'list': return TaskListSerializer return TaskSerializer
考察点 :掌握 get_serializer_class 的动态选择机制。
5. 如何在 ModelViewSet 中添加自定义动作?
问题描述 :为 TaskViewSet 添加一个自定义动作 mark_completed,通过 POST /api/tasks/<id>/mark_completed/ 将任务标记为已完成。提供代码示例。
答案提示:
-
使用
@action装饰器定义自定义动作。 -
示例:
pythonfrom rest_framework.decorators import action from rest_framework.response import Response class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer @action(detail=True, methods=['post']) def mark_completed(self, request, pk=None): task = self.get_object() task.completed = True task.save() serializer = self.get_serializer(task) return Response(serializer.data) -
detail=True表示该动作针对单个对象,URL 为/api/tasks/<id>/mark_completed/。
考察点 :理解 @action 装饰器和自定义端点的实现。
6. 如何在 ModelViewSet 中实现分页?
问题描述 :当任务列表很长时,如何为 TaskViewSet 的 list 操作启用分页,每页显示 10 条记录?
答案提示:
-
在
settings.py中配置全局分页,或在ViewSet中指定分页类。 -
示例(全局配置):
python# settings.py REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 } -
或者在
ViewSet中:pythonclass TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer pagination_class = PageNumberPagination page_size = 10 -
访问
/api/tasks/?page=2获取第二页数据。
考察点:掌握 DRF 的分页机制和配置。
7. 如何为 ModelViewSet 添加过滤功能?
问题描述 :如何让 TaskViewSet 支持通过查询参数过滤任务,例如 GET /api/tasks/?completed=true 只返回已完成的任务?
答案提示:
-
使用 DRF 的
django-filter扩展或自定义过滤。 -
安装
django-filter:pip install django-filter -
示例:
python# settings.py INSTALLED_APPS = [... , 'django_filters'] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] } # views.py class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer filterset_fields = ['completed'] -
访问
/api/tasks/?completed=true即可过滤。
考察点 :理解 DRF 的过滤后端和 django-filter 的使用。
难题 (3 道)
8. 如何在 ModelViewSet 中实现复杂的权限控制?
问题描述 :假设 Task 模型有一个 is_private 字段。只有任务的拥有者或超级用户可以查看/编辑私有任务(is_private=True),其他用户只能访问非私有任务。如何实现?
答案提示:
-
创建自定义权限类,继承
permissions.BasePermission。 -
示例:
pythonfrom rest_framework import permissions class TaskPermission(permissions.BasePermission): def has_object_permission(self, request, view, obj): if not obj.is_private: return True # 非私有任务对所有人可访问 return obj.owner == request.user or request.user.is_superuser class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer permission_classes = [permissions.IsAuthenticated, TaskPermission] def get_queryset(self): if self.request.user.is_superuser: return Task.objects.all() return Task.objects.filter( models.Q(is_private=False) | models.Q(owner=self.request.user) ) -
has_object_permission控制单个对象的访问权限,get_queryset过滤列表。
考察点:高级权限控制、自定义权限类和查询集过滤的结合。
9. 如何在 ModelViewSet 中处理嵌套序列化器?
问题描述 :假设 Task 模型与 Category 模型通过外键关联(task.category)。如何在 TaskViewSet 中返回任务时包含分类的详细信息,而不是仅返回分类 ID?
答案提示:
-
在序列化器中使用嵌套序列化器。
-
示例:
python# models.py class Category(models.Model): name = models.CharField(max_length=100) class Task(models.Model): title = models.CharField(max_length=200) category = models.ForeignKey(Category, on_delete=models.CASCADE) # serializers.py class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = ['id', 'name'] class TaskSerializer(serializers.ModelSerializer): category = CategorySerializer(read_only=True) class Meta: model = Task fields = ['id', 'title', 'category'] # views.py class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer -
为支持创建/更新任务时接收分类 ID,需重写序列化器的
create/update方法。
考察点:嵌套序列化器、只读字段和复杂数据结构的处理。
10. 如何优化 ModelViewSet 的性能?
问题描述 :假设 TaskViewSet 的 list 操作需要处理大量数据(例如 10 万条任务记录),如何优化性能以减少响应时间?
答案提示:
-
分页:启用分页(参考题目 6),限制每次返回的记录数。
-
选择性字段加载 :使用
select_related或prefetch_related优化数据库查询。pythonclass TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.select_related('category').all() serializer_class = TaskSerializer -
缓存 :使用 Django 的缓存框架或 DRF 的缓存装饰器。
pythonfrom rest_framework.decorators import cache_page class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer @cache_page(60 * 15) # 缓存 15 分钟 def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) -
索引 :在数据库中为常用查询字段(如
owner、completed)添加索引。 -
异步任务:对于耗时操作(如批量更新),使用 Celery 异步处理。
考察点:性能优化技巧,包括数据库查询优化、缓存和异步任务。