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:
pip install django djangorestframework
创建 Django 项目
- 创建一个新的 Django 项目:
django-admin startproject task_manager cd task_manager
- 创建一个 Django 应用:
python manage.py startapp tasks
- 将
tasks
应用和rest_framework
添加到task_manager/settings.py
的INSTALLED_APPS
中:INSTALLED_APPS = [ ... 'rest_framework', 'tasks', ]
3.2 定义模型
在 tasks/models.py
中定义一个简单的 Task
模型:
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__
:返回任务的字符串表示,便于调试。
运行迁移命令以创建数据库表:
python manage.py makemigrations
python manage.py migrate
3.3 创建序列化器(Serializer)
序列化器负责将模型数据转换为 JSON 格式(或从 JSON 转换为模型)。在 tasks/serializers.py
中创建:
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
:
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
中:
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 开发服务器:
python 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
中:
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
中启用认证:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], }
示例 2:过滤查询集
只显示当前用户的任务:
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
字段:from django.contrib.auth.models import User class Task(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) ...
示例 3:自定义序列化器行为
假设你想在 list
操作时只返回部分字段:
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 前缀和视图集。 - 示例:
from 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
过滤查询集。 - 示例:
class 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
返回不同的序列化器。 - 示例:
class 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
装饰器定义自定义动作。 - 示例:
from 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
中指定分页类。 - 示例(全局配置):
# settings.py REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 }
- 或者在
ViewSet
中:class 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
- 示例:
# 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
。 - 示例:
from 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?
答案提示:
- 在序列化器中使用嵌套序列化器。
- 示例:
# 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
优化数据库查询。class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.select_related('category').all() serializer_class = TaskSerializer
- 缓存:使用 Django 的缓存框架或 DRF 的缓存装饰器。
from 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 异步处理。
考察点:性能优化技巧,包括数据库查询优化、缓存和异步任务。