Django REST Framework 中 ModelViewSet 的接口方法及参数详解,继承的方法和核心类方法,常用查询方法接口
第一部分(ModelViewSet)
一、ModelViewSet 的继承结构
ModelViewSet
继承自以下类:
ModelViewSet = (
CreateModelMixin + # 创建
RetrieveModelMixin + # 检索单个
UpdateModelMixin + # 更新
DestroyModelMixin + # 删除
ListModelMixin + # 列表
GenericViewSet # 基础视图集
)
二、默认接口方法及参数
以下方法由 mixins 提供,支持 RESTful 标准操作:
方法名 | HTTP 方法 | 参数说明 | 作用 |
---|---|---|---|
list(ListModelMixin) | GET | self, request, *args, **kwargs | 获取资源列表 |
| POST | self, request, *args, **kwargs | 创建新资源 |
| GET | self, request, *args, **kwargs (需 pk 或 slug ) | 获取单个资源 |
| PUT /PATCH | self, request, *args, **kwargs (需 pk 或 slug ) | 全量/部分更新资源 |
| DELETE | self, request, *args, **kwargs (需 pk 或 slug ) | 删除资源 |
参数统一说明:
-
request
: Django 请求对象,包含请求数据 (request.data
) 和用户信息 (request.user
)。 -
*args
: URL 中捕获的位置参数(如路径参数)。 - ==
**‌==kwargs
: URL 中捕获的关键字参数(如pk=1
)。
三、核心类方法及参数详解
以下方法由 GenericViewSet
和 GenericAPIView
提供,用于控制视图行为:
方法名 | 参数 | 返回值 | 作用 |
---|---|---|---|
get_queryset() | 无参数,但可通过 self.request 访问请求上下文 | QuerySet | 返回默认查询集(可覆盖以实现动态过滤) |
get_object() | 无参数,从 URL 参数中提取 pk 或 slug | 模型实例 | 获取单个对象,支持 lookup_field 自定义查询字段 |
get_serializer() | instance=None, data=None, many=False, partial=False, **kwargs | 序列化器实例(serializer ) | 初始化序列化器,用于请求数据验证和响应数据生成(对需要更新的数据进行验证并返回验证后的数据) |
get_serializer_class() | 无参数,但可通过 self.action 判断当前动作(如 create /update ) | 序列化器类 | 动态选择序列化器(例如不同动作使用不同序列化器) |
perform_create() | serializer (序列化器实例) | 无,直接保存对象到数据库 | 在创建对象前/后执行额外操作(如设置 created_by=request.user ) |
perform_update() | serializer (序列化器实例) | 无,直接更新对象到数据库 | 在更新对象前/后执行额外操作(如记录日志) |
perform_destroy() | instance (要删除的模型实例) | 无,直接删除对象 | 在删除对象前/后执行额外操作(如级联删除关联数据) |
filter_queryset() | queryset (基础查询集) | 过滤后的 QuerySet | 应用所有已配置的过滤器(如 DjangoFilterBackend ) |
四、关键参数扩展说明
1. get_object()
的底层参数
-
lookup_field
: 默认是'pk'
,可通过类属性修改为其他字段(如slug
)。 -
lookup_url_kwarg
: 默认与lookup_field
相同,指定从 URL 中提取参数的名称。
示例:
class BookViewSet(ModelViewSet):
lookup_field = 'isbn' # 模型字段
lookup_url_kwarg = 'book_isbn' # URL 参数名
2. get_serializer()
的参数
-
instance
: 要更新的模型实例(用于update
操作)。 -
data
: 请求数据(request.data
)。 -
partial
: 是否允许部分更新(True
对应PATCH
请求)。- 从请求参数中获取'partial'标识,当值为True时表示PATCH请求(部分更新字段),False时表示PUT请求(完整更新)。通过控制该参数,DRF的序列化器会决定是否进行全字段验证。
五、权限与节流相关方法
方法名 | 参数 | 作用 |
---|---|---|
get_permissions() | 无 | 返回权限类列表(如 [IsAuthenticated] ) |
get_throttles() | 无 | 返回节流类列表(如 [UserRateThrottle] ) |
六、自定义场景示例
1. 动态过滤查询集
def get_queryset(self):
# 只返回当前用户创建的书籍
return Book.objects.filter(created_by=self.request.user)
2. 按动作选择序列化器
def get_serializer_class(self):
if self.action == 'list':
return BookListSerializer
return BookDetailSerializer
3. 记录操作日志
def perform_create(self, serializer):
book = serializer.save(created_by=self.request.user)
log_action('create', self.request.user, book)
七、完整方法调用流程图
sequenceDiagram
participant Client
participant ViewSet
Client->>ViewSet: HTTP 请求 (如 GET /books/)
ViewSet->>ViewSet: determine_action()
ViewSet->>ViewSet: get_queryset()
ViewSet->>ViewSet: filter_queryset()
ViewSet->>ViewSet: get_serializer()
ViewSet->>ViewSet: perform_动作()
ViewSet->>Client: 返回 Response
二部分(查询filter)
一、模型关系假设
# models.py
class PracticeType(models.Model):
p_type = models.CharField(max_length=50) # 练习类型字段
is_active = models.BooleanField(default=True)
class QuestionGroup(models.Model):
name = models.CharField(max_length=100)
g_type = models.ForeignKey(PracticeType, on_delete=models.CASCADE) # 外键关联
created_at = models.DateTimeField(auto_now_add=True)
class Question(models.Model):
group = models.ForeignKey(QuestionGroup, on_delete=models.CASCADE, related_name='questions')
content = models.TextField()
score = models.IntegerField()
二、查询方法详解(表格)
方法 | 示例代码 | 参数说明 | 作用描述 |
---|---|---|---|
filter() | QuestionGroup.objects.filter(g_type__p_type='数学') | g_type__p_type : 跨模型查询 PracticeType.p_type 字段 | 筛选 p_type 为 "数学" 的题组 |
QuestionGroup.objects.filter(created_at__year=2024) | created_at__year : 提取日期字段的年份 | 筛选 2024 年创建的题组 | |
exclude() | QuestionGroup.objects.exclude(g_type__is_active=False) | g_type__is_active : 排除关联模型中 is_active=False 的题组 | 排除关联到非激活练习类型的题组 |
order_by() | QuestionGroup.objects.order_by('-created_at', 'g_type') | -created_at : 按创建时间倒序;g_type : 按外键的默认排序(如 p_type ) | 先按创建时间倒序,再按练习类型排序 |
values() | QuestionGroup.objects.values('id', 'g_type__p_type') | id : 返回题组 ID;g_type__p_type : 返回关联练习类型的 p_type 字段 | 获取题组 ID 及其关联的练习类型名称 |
annotate() | QuestionGroup.objects.annotate(total_score=Sum('questions__score')) | total_score=Sum('questions__score') : 计算每个题组下所有题目的总分 | 为每个题组添加总分注解字段 total_score |
QuestionGroup.objects.annotate(avg_score=Avg('questions__score')) | avg_score=Avg('questions__score') : 计算每个题组下题目的平均分 | 为每个题组添加平均分注解字段 avg_score |
三、参数详细说明
1. ==filter(**‌==kwargs)
- 过滤
- 参数语法:
字段__操作符=值
(如created_at__year=2024
) - 跨模型查询: 使用双下划线
__
跳转关联模型字段(如g_type__p_type
)。 - 示例:
# 查询 p_type 为 "物理" 的题组 groups = QuestionGroup.objects.filter(g_type__p_type='物理') # 查询 2023 年之后创建的题组 groups = QuestionGroup.objects.filter(created_at__year__gte=2023)
2. ==exclude(**‌==kwargs)
- 排除
- 参数语法: 同
filter()
,但排除满足条件的对象。 - 示例:
# 排除关联到非激活练习类型的题组 groups = QuestionGroup.objects.exclude(g_type__is_active=False) # 排除 p_type 为 "化学" 的题组 groups = QuestionGroup.objects.exclude(g_type__p_type='化学')
3. order_by(*fields)
- 排序
- 参数语法: 字段名前加
-
表示倒序(如-created_at
)。 - 跨模型排序: 支持按关联模型字段排序(如
g_type__p_type
)。 - 示例:
# 按练习类型名称升序,再按创建时间倒序 groups = QuestionGroup.objects.order_by('g_type__p_type', '-created_at')
4. values(*fields)
- 取字段
- 参数语法: 指定要返回的字段或关联字段(如
g_type__p_type
)。 - 返回结果: 字典列表(而非模型对象)。
- 示例:
# 获取所有题组的 ID、名称及其关联的练习类型名称 data = QuestionGroup.objects.values('id', 'name', 'g_type__p_type')
5. annotate(别名=聚合函数)
- 注解
- 参数语法: 使用聚合函数(如
Count
,Sum
,Avg
)生成计算字段。 - 跨模型聚合: 通过关联模型的
related_name
(如questions
)访问子对象。 - 示例:
from django.db.models import Count, Sum, Avg # 统计每个题组的问题数量 groups = QuestionGroup.objects.annotate(question_count=Count('questions')) # 计算每个题组的总分和平均分 groups = QuestionGroup.objects.annotate( total_score=Sum('questions__score'), avg_score=Avg('questions__score') )
四、高级用法
1. 链式调用
# 查询 2024 年创建的数学题组,按总分降序排列
groups = QuestionGroup.objects.filter(
g_type__p_type='数学',
created_at__year=2024
).annotate(
total_score=Sum('questions__score')
).order_by('-total_score')
2. 跨多级模型查询
# 查询所有题目分数大于 80 的题组
groups = QuestionGroup.objects.filter(questions__score__gt=80)
3. 空值处理
# 查询没有关联任何问题的题组
groups = QuestionGroup.objects.filter(questions__isnull=True)
五、性能优化
-
select_related
: 用于外键或一对一字段的预加载(减少查询次数)。# 预加载 g_type 关联的 PracticeType 对象 groups = QuestionGroup.objects.select_related('g_type').filter(g_type__p_type='数学')
-
prefetch_related
: 用于多对多或一对多字段的预加载。# 预加载题组下的所有问题 groups = QuestionGroup.objects.prefetch_related('questions').annotate(total_score=Sum('questions__score'))