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 ) |
get_object()
的底层参数lookup_field
: 默认是 'pk'
,可通过类属性修改为其他字段(如 slug
)。lookup_url_kwarg
: 默认与 lookup_field
相同,指定从 URL 中提取参数的名称。示例:
class BookViewSet(ModelViewSet):
lookup_field = 'isbn' # 模型字段
lookup_url_kwarg = 'book_isbn' # URL 参数名
get_serializer()
的参数instance
: 要更新的模型实例(用于 update
操作)。data
: 请求数据(request.data
)。partial
: 是否允许部分更新(True
对应 PATCH
请求)。
方法名 | 参数 | 作用 |
---|---|---|
get_permissions() |
无 | 返回权限类列表(如 [IsAuthenticated] ) |
get_throttles() |
无 | 返回节流类列表(如 [UserRateThrottle] ) |
def get_queryset(self):
# 只返回当前用户创建的书籍
return Book.objects.filter(created_by=self.request.user)
def get_serializer_class(self):
if self.action == 'list':
return BookListSerializer
return BookDetailSerializer
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
# 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 |
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)
exclude(**==kwargs)
- 排除filter()
,但排除满足条件的对象。
# 排除关联到非激活练习类型的题组
groups = QuestionGroup.objects.exclude(g_type__is_active=False)
# 排除 p_type 为 "化学" 的题组
groups = QuestionGroup.objects.exclude(g_type__p_type='化学')
order_by(*fields)
- 排序-
表示倒序(如 -created_at
)。g_type__p_type
)。
# 按练习类型名称升序,再按创建时间倒序
groups = QuestionGroup.objects.order_by('g_type__p_type', '-created_at')
values(*fields)
- 取字段g_type__p_type
)。
# 获取所有题组的 ID、名称及其关联的练习类型名称
data = QuestionGroup.objects.values('id', 'name', 'g_type__p_type')
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')
)
# 查询 2024 年创建的数学题组,按总分降序排列
groups = QuestionGroup.objects.filter(
g_type__p_type='数学',
created_at__year=2024
).annotate(
total_score=Sum('questions__score')
).order_by('-total_score')
# 查询所有题目分数大于 80 的题组
groups = QuestionGroup.objects.filter(questions__score__gt=80)
# 查询没有关联任何问题的题组
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'))