第一部分(ModelViewSet)
一、ModelViewSet 的继承结构
ModelViewSet
继承自以下类:
python
ModelViewSet = (
CreateModelMixin + # 创建
RetrieveModelMixin + # 检索单个
UpdateModelMixin + # 更新
DestroyModelMixin + # 删除
ListModelMixin + # 列表
GenericViewSet # 基础视图集
)
二、默认接口方法及参数
以下方法由 mixins 提供,支持 RESTful 标准操作:
方法名 | HTTP 方法 | 参数说明 | 作用 |
---|---|---|---|
**list(ListModelMixin) ** |
GET |
self, request, *args, **kwargs |
获取资源列表 |
**create ** **(CreateModelMixin) ** |
POST |
self, request, *args, **kwargs |
创建新资源 |
**retrieve ** **(RetrieveModelMixin) ** |
GET |
self, request, *args, **kwargs (需 pk 或 slug ) |
获取单个资源 |
**update ** **(UpdateModelMixin) ** |
PUT /PATCH |
self, request, *args, **kwargs (需 pk 或 slug ) |
全量/部分更新资源 |
**destroy ** **(DestoryModelMixin) ** |
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 中提取参数的名称。
示例:
python
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. 动态过滤查询集
python
def get_queryset(self):
# 只返回当前用户创建的书籍
return Book.objects.filter(created_by=self.request.user)
2. 按动作选择序列化器
python
def get_serializer_class(self):
if self.action == 'list':
return BookListSerializer
return BookDetailSerializer
3. 记录操作日志
python
def perform_create(self, serializer):
book = serializer.save(created_by=self.request.user)
log_action('create', self.request.user, book)
七、完整方法调用流程图
python
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)
一、模型关系假设
python
# 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
)。 -
示例 :
python# 查询 p_type 为 "物理" 的题组 groups = QuestionGroup.objects.filter(g_type__p_type='物理') # 查询 2023 年之后创建的题组 groups = QuestionGroup.objects.filter(created_at__year__gte=2023)
2. ==**exclude(**‌==kwargs)
** - 排除
-
参数语法 : 同
filter()
,但排除满足条件的对象。 -
示例 :
python# 排除关联到非激活练习类型的题组 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
)。 -
示例 :
python# 按练习类型名称升序,再按创建时间倒序 groups = QuestionGroup.objects.order_by('g_type__p_type', '-created_at')
4. **values(*fields)
** - 取字段
-
参数语法 : 指定要返回的字段或关联字段(如
g_type__p_type
)。 -
返回结果: 字典列表(而非模型对象)。
-
示例 :
python# 获取所有题组的 ID、名称及其关联的练习类型名称 data = QuestionGroup.objects.values('id', 'name', 'g_type__p_type')
5. **annotate(别名=聚合函数)
** - 注解
-
参数语法 : 使用聚合函数(如
Count
,Sum
,Avg
)生成计算字段。 -
跨模型聚合 : 通过关联模型的
related_name
(如questions
)访问子对象。 -
示例 :
pythonfrom 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. 链式调用
python
# 查询 2024 年创建的数学题组,按总分降序排列
groups = QuestionGroup.objects.filter(
g_type__p_type='数学',
created_at__year=2024
).annotate(
total_score=Sum('questions__score')
).order_by('-total_score')
2. 跨多级模型查询
python
# 查询所有题目分数大于 80 的题组
groups = QuestionGroup.objects.filter(questions__score__gt=80)
3. 空值处理
python
# 查询没有关联任何问题的题组
groups = QuestionGroup.objects.filter(questions__isnull=True)
五、性能优化
-
**
select_related
** : 用于外键或一对一字段的预加载(减少查询次数)。python# 预加载 g_type 关联的 PracticeType 对象 groups = QuestionGroup.objects.select_related('g_type').filter(g_type__p_type='数学')
-
**
prefetch_related
** : 用于多对多或一对多字段的预加载。python# 预加载题组下的所有问题 groups = QuestionGroup.objects.prefetch_related('questions').annotate(total_score=Sum('questions__score'))