第一部分(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'))