DRF基础
- DRF基础
- 【零】DRF在Django项目中的使用
- 【一】View
- 【二】序列化组件
- 【三】请求与响应
- 【四】视图组件
- 【五】路由组件
DRF基础
【零】DRF在Django项目中的使用
【1】导入
# DRF需要使用pip install 安装
pip install djangorestframework
- DRF(Django Rest Framework)是一个用于构建 Web API 的工具包,它是基于 Django 框架的一个第三方应用(app)
- 在 Django 项目中,一个应用(app)通常是一个具有特定功能的模块,它包含了模型、视图、模板等组件,用于实现特定的功能或业务逻辑。
【2】使用
- 使用drf时,需要先在
settings.py
文件中进行注册app
# settings.py
INSTALLED_APPS = [
...
'rest_framework'
]
【一】View
- restful规范所需5个接口
- 查询所有
- 添加
- 查询单条数据
- 更新单条
- 删除单条
【1】基于Django的View实现5个接口
- 预处理请求数据的装饰器代码
- 【注】该版本如果使用post请求,需要注释掉csrf中间件
【2】基于DRF.APIView + Response实现5个接口
-
使用Django自带的View我们可以发现,其对请求数据的处理不够完善
-
而在DRF中,其帮我们处理了请求数据,能够让我们可以通过【request.data】直接获取到三种编码格式处理后的数据
-
【request.data】中的数据可能为【querydict】或【dict】对象
-
代码预览
【2.1】APIView源码分析
- 视图类调用时,就是执行【as_view()】方法
- 【as_view()】就是我们查看APIView源码的入口
【2.1.1】DRF的APIView的as_view()
【2.1.2】Django的View的as_view()
【2.1.3】DRF的APIView的dispatch()
【2.1.3.1】包装新的request
- Request类实例化对象的
__init__
方法
【2.1.4】APIView的request对象
- APIView的request对象相较于django的request对象多了一些属性
- 常用的(以下的reuqest均为APIView的request对象)
request.data
:获取请求体中的数据,以QueryDict形式或Dict形式返回request.query_params
:与django的request对象中的.GET
方法一致
- 当获取APIView的request对象中没有的属性时,将会触发
__getattr__
方法
【2.1.5】源码存疑的地方
- 如果再调用
self.__getattribute__
获取属性,好像没有什么必要,肯定是会报错的,为什么要多做一个异常捕获,然后再抛出一个异常呀
【二】序列化组件
【1】序列化与反序列化的概念
-
序列化(Serialization):将对象转换为可传输或可存储的格式的过程。在序列化过程中,对象的属性和数据被转换为一个字节流或字符串,以便在网络上传输或保存到文件中。常见的序列化格式包括 JSON、XML、Protocol Buffers 等。序列化后的数据可以在不同的系统、编程语言或应用程序之间进行交换和共享。
-
反序列化(Deserialization):将序列化后的数据恢复为原始对象的过程。在反序列化过程中,从序列化格式(例如 JSON 字符串)中解析出对象的属性和数据,并重新构建原始对象。反序列化的过程与序列化过程相反,它将序列化后的数据转换回原始对象,以便进行进一步的处理、操作或显示。
-
简单来说:
-
序列化:将对象转换为可传输的格式
-
反序列化:从 JSON 字符串、XML 文档、字节流等中解析出对象的属性和数据。
-
【2】序列化类(Serializer)
【2.1】定义序列化类
# 定义序列化类需要继承drf.Serializer
from rest_framework import serializers
'''简单示例'''
class MySerializer(serializers.Serializer):
字段名 = serializers.字段类型(字段参数)
'''实例'''
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
def create(self, validated_data):
"""
根据提供的验证过的数据创建并返回一个新的`Snippet`实例。
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
根据提供的验证过的数据更新和返回一个已经存在的`Snippet`实例。
"""
instance.title = validated_data.get('title', instance.title)
return instance
- 序列化器类的第一部分定义了序列化/反序列化的字段。
create()
和update()
方法定义了在调用serializer.save()
时如何创建和修改完整的实例。
【2.2】常见字段及参数
【2.2.1】常用字段类型
字段 | 字段构造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
【2.2.2】选项参数
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
【2.2.3】通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
【3】序列化类的基本操作
【3.1】序列化
# 导入构建好的序列化类
from xx import Serializer
# 创建序列化类对象
ser = Serializer(instance=obj对象,data=request.data,...)
# 获取序列化后的数据 # 以json格式返回
ser.data
【3.1.1】序列化对象的常用参数
- instance:要序列化的模型实例。如果需要对现有对象进行序列化,则传递该实例。
- data:要反序列化的数据。如果需要从数据中创建对象,则传递该数据。通常用于创建或更新对象。
- context:上下文数据,可以在序列化器的各个方法中使用。通常用于在序列化器之间传递额外的信息。
- many:指定是否序列化多个对象。默认为 False。如果设置为 True,则可以序列化多个对象的查询集。
- partial:指定是否部分更新对象。默认为 False。如果设置为 True,则可以部分更新对象而不需要提供所有字段的值。
【3.2】反序列化校验
- 序列化器在反序列化时通常会执行一系列验证操作,以确保输入的数据符合预期的格式和约束。
- 这些验证功能可以帮助确保用户提供的数据是有效的,并且可以在存储到数据库之前进行预处理。
【3.2.1】校验级别
【3.2.1.1】字段级别的验证
class BookSerializer(serializers.Serializer):
name = serializers.CharField(min_length=5, max_length=32, error_messages={
'min_length': '最少不得少于5个字符',
'max_length': '最多不得多于32个字符'
})
price = serializers.IntegerField(min_value=10, max_value=999, error_messages={
'min_value': '最少不得少于10元',
'max_value': '最多不得多于999元'
})
publish = serializers.CharField(min_length=3, max_length=255, error_messages={
'min_length': '最少不得少于3个字符',
'max_length': '最多不得多于255个字符'
})
【3.2.1.2】自定制校验器validators
- 与forms组件一样,可以指定
validators
参数自定制验证器
def func(value):
'''验证数据格式'''
if xxx:
# 指定条件
# 抛出异常
raise ValidationError("错误信息")
return value
class MySerializer(serializers.Serializer):
# 可以指定多个验证器
name = serializers.CharField(max_length=32,validators=[func,func2...])
【3.2.1.3】钩子函数
- 局部钩子:
validate_字段(self,字段)
- 全局钩子:
validate(self,attrs)
- 校验失败 抛出指定异常
ValidationError
from rest_framework.exceptions import ValidationError
# 局部钩子
def validate_task_name(self, name: str):
if 'sb' in name:
# 抛出指定异常
raise ValidationError('不得包含侮辱性词汇')
# 注意需要返回该字段
return name
# 全局钩子
def validate(self, attrs):
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名不得与出版社名一致')
# 需要返回字段
return attrs
【3.2.2】校验完成的数据 校验失败的错误信息
-
ser.is_valid()
:返回布尔值,True表示所有数据验证通过,False表示有错误数据- 通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应
-
ser.data
:包含了序列化后字段及其对应数值的字典,可以直接用于生成 JSON 或其他格式的响应数据 -
ser.errors
:校验失败的错误提示信息,由error_message
参数指定或使用默认
# views.py
'''不需要携带参数的视图类'''
class BookViewCR(APIView):
def get(self, request):
book_all = Book.objects.all()
# 将需要序列化的数据给 【instance】参数 # 如果是多条 需要传递 【many】参数
ser = BookSerializer(instance=book_all, many=True)
# 返回 一个数组【[{},{}]】 # 指定了many参数将视为多条数据 并按照restful规范返回数组格式
return Response({'code': 100,'msg': 'get','results': ser.data})
def post(self, request):
data = request.data
# 新增数据 # 直接将数据传入data参数中
ser = BookSerializer(data=data)
if ser.is_valid():
# 校验数据
# 校验通过 # 执行保存方法
ser.save()
return Response({'code': 100, 'msg': '添加成功', 'result': ser.data})
else:
# 校验失败
# 返回错误信息
return Response({'code': 101, 'msg': ser.errors})
'''需要携带 pk 参数 的视图类'''
class BookViewRUD(APIView):
# 查询指定pk数据
def get(self, request, tid):
# 获取对象
book = Book.objects.filter(pk=tid).first()
if not book:
return Response({'code': 101, 'msg': '当前书籍不存在'})
# 将查询到的对象传入instance
ser = BookSerializer(instance=book)
# 返回序列化后的数据
return Response({'code': 100, 'msg': f'查询指定【{tid}】成功', 'result': ser.data})
# 修改指定数据
def put(self, request, tid):
# 获取对象
book = Book.objects.filter(pk=tid).first()
if not book:
return Response({'code': 101, 'msg': '当前书籍不存在'})
# 将对象传入instance # 将需要更新的数据传入data
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
# 进行数据校验
# 数据校验通过调用save方法保存
ser.save()
return Response({'code': 100, 'msg': f'修改指定【{tid}】成功', 'result': ser.data})
else:
# 校验失败返回错误信息
return Response({'code': 101, 'msg': ser.errors, })
【3.2】反序列化保存 create
update
- 序列化器判断是新建对象还是更新对象的标准就是,是否传递了
instance
参数,数据将由data
参数接受
class xxx(serializers.Serializer):
# ...
def create(self, validated_data):
'''可以对校验过的数据进行再次处理'''
# 新增数据
book = Book.objects.create(**validated_data)
'''
title = validated_data.pop('title')
title += '爆款'
# 为标题加上爆款二字
Book.objects.create(**validated_data,title=title)
'''
return book
def update(self, instance, validated_data):
# 由于instance是一个对象,没办法直接使用 【queryset.update(**kwargs)】方法
# 所以需要使用反射的方法为对象更新属性
for key in validated_data.keys():
setattr(instance, key, validated_data.get(key))
instance.save()
# 将对象返回
return instance
【3.2.1】save(**kwargs)
- 对序列化器进行
save()
保存时,可以额外传递数据,这些数据可以在create()
和update()
中的validated_data
参数获取到
# views.py
class MyView(APIView):
def post(self,request):
ser = Ser(data=reuqest.data)
ser.is_valid(raise_exception=True)
ser.save(other='xxx')
###############################################
# serializer.py
class Ser(serializer.Serializer):
...
def create(self,validated_data):
other = validated_data.pop('other')
...
【4】source参数
source
参数用于指定要序列化的字段的源。通常,它用于在序列化器中访问模型实例的属性或方法,并将其值包含在序列化后的数据中。- 当你需要访问模型实例的属性或方法,并将其值序列化到响应中时,可以使用
source
参数来指定该属性或方法的名称。这个参数告诉序列化器从哪里获取数据以进行序列化。
【4.1】source参数跨表查询
# models.py
class Books(models.Model):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
# 如果是外键字段 # 不使用source参数指定将会返回整个publish模型实例
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
###############################################
# serializer.py
class Ser(serializers.Serializer):
...
'''可以指定返回外键字段对象所对应的属性值'''
publish_name = serializers.CharField(source='publish.name')
publish_addr = serializers.CharField(source='publish.addr')
'''更改字段序列化时显示的名称'''
book_name = serializers.CharField(source='title') # book_name的值就是模型表中的title字段
【4.2】source参数指定执行模型表中的方法
'''指定执行模型表中的方法'''
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
def name2desc(self):
'''执行一些代码'''
return self.name + self.desc
############################################
# ser.py
class TaskSer(serializers.Serializer):
# 通过source参数指定模型表中方法 # 传对应的函数名即可
name_desc = serializers.CharField(source='name2desc')
【4.3】source参数指定显示模型表中字段名称
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
############################################
# ser.py
class TaskSer(serializers.Serializer):
# task_name将作为序列化返回给前端的json数据中展示name字段的键
task_name = serializers.CharField(source='name')
【4.4】总结
# ser.py
from rest_framework import serializers
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
# 可以使用source参数自定义显示给前端的json数据中的键
task_name = serializers.CharField(source='name')
# 模型表中的方法也可以使用同名的字段名进行调用
name2desc = serializers.CharField()
# 也可以使用source参数执行模型表中的方法
name_desc = serializers.CharField(source='name2desc')
# 直接使用外键字段将回返回当前模型表对象对应的外键对象
user = serializers.CharField()
# 可以使用source参数进行跨表查询
user_name = serializers.CharField(source='user.username')
###############################################
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ForeignKey(to='User', on_delete=models.CASCADE, default=1)
def name2desc(self):
'''执行一些代码'''
return self.name + self.desc
class User(models.Model):
username = models.CharField(max_length=32, default='aaa')
【5】使用 SerializerMethodField 定制字段
# 基本语法
字段 = serializers.SerializerMethodField()
def get_字段(self,obj):
'''此处的obj就是当前正在序列化的模型表对象'''
return ...
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 可以使用source参数进行跨表查询
user_name = serializers.CharField(source='user.username')
xxx = serializers.SerializerMethodField()
def get_xxx(self, obj):
'''
此处的self为序列化类的对象,应当返回模型对象中的数据
:param obj: 当前模型表对象
:return: 想要展示的值
'''
return obj.desc + '这是描述哟'
【6】使用 子序列化 定制字段
- 使用子序列化以实现序列化类的复用
class UserSer(serializers.Serializer):
'''user表的序列化类'''
username = serializers.CharField()
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 使用子序列化以实现序列化类的复用
# 如果字段名与模型表中的外键字段一致 # 当调用子序列化时,将会自动将该外键对象传入子序列化类中
user = UserSer()
# 可以使用source参数指定该字段对应的外键字段 # 那么子序列化类将会自动识别外键对象
user_obj = UserSer(source='user')
- 如果既不使用与外键字段同名,且不使用source参数指定,将会报错
【7】ListField和DictField
ListField
用于序列化和反序列化列表类型的数据。- 当你序列化数据时,
ListField
会将列表中的每个元素序列化为相应的格式,例如 JSON 数组 - 当你反序列化时,
ListField
字段将可以接受数组类型的数据
- 当你序列化数据时,
DictField
用于序列化和反序列化字典类型的数据。- 当你序列化数据时,
DictField
会将字典中的键值对序列化为相应的格式,例如 JSON 对象。 - 当你反序列化时,
DictField
字段将可以接受字典类型的数据
- 当你序列化数据时,
【7.1】序列化使用场景
- 在模型表中,定义某些方法,返回列表或字典格式
- 【注】对于多对多字段,不能直接使用
ListField
来进行序列化,可以使用SerializerMethodField
定义方法- 在模型表中定义方法返回列表
- 使用子序列化并传递
many=True
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ForeignKey(to='User', on_delete=models.CASCADE, default=1)
def num_list(self):
return [1, 2, 3]
def info_dic(self):
return {'id': 1, 'username': self.user.username}
##################################################
# ser.py
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 所有的模型表中的字段类型都可以使用CharField强转 # 但是此时该字段就只是字符串了
l_str = serializers.CharField(source='num_list')
# 使用source参数指定
l = serializers.ListField(source='num_list')
# 使用同名的字段
num_list = serializers.ListField()
info_dic = serializers.DictField()
dic = serializers.DictField(source='info_dic')
- 多对多外键字段,序列化演示
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# # 多对多外键字段
# user = serializers.ListField() # 将会报错
# 使用 SerializerMethodField
user_list = serializers.SerializerMethodField()
def get_user_list(self, obj):
return [user.username for user in obj.user.all()]
# 使用子序列化
user_list2 = UserSer(source='user', many=True)
【7.2】反序列化使用场景
- 常用于多对多字段
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ManyToManyField(to='User')
class User(models.Model):
username = models.CharField(max_length=32, default='aaa')
#################################################
# ser.py
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 使用ListField接收列表数据 # write_only表示该字段只进行反序列化 # 只用来接收前端的数据并保存
user = serializers.ListField(write_only=True)
# user = serializers.CharField(write_only=True) # "Not a valid string." # 将会报错
# 使用DictField接收字典形式数据
task_info = serializers.DictField(write_only=True)
def create(self, validated_data):
# 使用post创建对象时,必须重写create方法
# 否则会报错
print(validated_data)
# {'name': 'task3', 'desc': 'task3-desc', 'user': [1, 2], 'task_info': {'time': 'xxxx', 'style': 'aaaa'}}
return validated_data # 正常需要返回创建号的对象 # 此处将会返回序列化类序列化的数据
#################################################
# views.py
class TaskView(APIView):
def get(self, request):
ser = TaskSer(Task.objects.all(), many=True)
return Response(ser.data)
def post(self, request):
ser = TaskSer(data=request.data)
# 反序列化校验
ser.is_valid(raise_exception=True)
# 调用create方法
ser.save()
return Response(ser.data if ser.is_valid() else ser.errors)
【8】read_only
和write_only
- 当我们希望某些字段只执行序列化,也就是返回给前端时,可以使用
read_only
- 当我们希望某些字段只执行反序列化,也就是要求前端传值交由我们保存,可以使用
write_only
- 默认不填代表着,该字段既做序列化又做反序列化
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 只做序列化,将用户信息展示给前端
user = UserSer(many=True, read_only=True)
# 只做反序列化,需要用户传值
user_l = serializers.ListField(write_only=True)
task_info = serializers.DictField(write_only=True)
【补充】其他知识
【1】字段参数validators
- 在字段中添加validators选项参数,可以补充验证行为,如
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
title = serializers.CharField(label='名称', max_length=20, validators=[about_django])
pub_date = serializers.DateField(label='发布日期', required=False)
read = serializers.IntegerField(label='阅读量', required=False)
comment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
【2】反序列化校验的三层校验
- 执行字段内部的校验
- max_length / min_length 等
- validators :是一个列表,可以传多个校验函数,将会依次执行列表中的所有校验函数
- 执行局部钩子:
validate_字段
- 执行全局钩子:
validate
【3】无法直接使用source参数反向查询
- 直接使用
source
参数是无法实现复杂的反向查询的。source
参数通常用于简单的字段访问,例如直接从模型实例的属性中获取数据。 - 如果需要进行复杂的反向查询,例如从关联模型中获取相关对象列表,需要使用
SerializerMethodField
并定义一个方法来实现
【9】模型类序列化类(ModelSerializer)
- DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
- ModelSerializer与常规的Serializer相同,但提供了:
- 基于模型类自动生成一系列字段
- 基于模型类自动为Serializer生成validators,比如unique_together
- 包含默认的create()和update()的实现
from rest_framework.serializers import ModelSerializer
class XXX(ModelSerializer):
# 不在模型表中的字段
字段 = serializers.CharField()
class Meta:
# 如果是模型表中的字段 # 可以在Meta中的fields参数中指定即可
model=对应的表
# 模型表中的字段将于序列化类的字段类型一一对应
fields = 需要使用的字段
#### 【注】此处的fields需要注意,不仅仅要填写在模型表中的字段,还需要填写额外的字段 ####
extra_kwargs = {
'字段':{'额外添加的参数'}
}
- 【注】
fields
是所有使用到的字段
from .models import Task
class TaskSerV2(serializers.ModelSerializer):
# 只做反序列化,需要用户传值
user_l = serializers.ListField(write_only=True)
task_info = serializers.DictField(write_only=True)
aaa = serializers.CharField(source='name')
class Meta:
model = Task
fields = '__all__' # __all__ 表示模型表中的所有字段
# fields = ['id','name','aaa','desc'] # 相当于
extra_kwargs = {
'name': {'max_length': 10}
}
'''如果所需字段就是表中需要的,就不需要重写create方法了'''
def create(self, validated_data):
# 由于Task表中并不需要下面两个字段,所以去除掉
validated_data.pop('user_l')
validated_data.pop('task_info')
# 调用ModelSerializer中的create方法
task = super().create(validated_data)
return task
【三】请求与响应
【1】请求
【1.1】Request对象
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None)
关键字参数:
- request(HttpRequest). 原始请求实例。
- parsers(list/tuple). 用于解析请求内容的解析器。
- authenticators(list/tuple). 用于尝试验证请求用户身份的验证器。
- negotiator(None). 协商者,用于内容协商。
- parser_context(None). 解析器上下文,用于解析器的上下文信息。
【1.1.1】常用属性
-
.data
-
request.data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和request.FILES
属性,但提供如下特性:-
包含了解析之后的文件和非文件数据
-
包含了对POST、PUT、PATCH请求方式解析后的数据
-
利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
-
-
-
.query_params
request.query_params
与Django标准的request.GET
相同,只是更换了更符合restful规范的名称,搜索参数
【1.2】Parser解析器
- REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中
- Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
- 无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
【1.2.1】三种解析器模块
模块 | 描述 | 请求编码格式 |
---|---|---|
JSONParser | 用于解析 JSON 请求内容。request.data 将被填充为一个数据字典。 | application/json |
FormParser | 用于解析 HTML 表单内容。request.data 将被填充为一个数据 QueryDict。通常与 MultiPartParser 一起使用以完全支持 HTML 表单数据。 | application/x-www-form-urlencoded |
MultiPartParser | 用于解析多部分 HTML 表单内容,支持文件上传。request.data 和 request.FILES 将分别被填充为一个 QueryDict 和 MultiValueDict。通常与 FormParser 一起使用以完全支持 HTML 表单数据。 | multipart/form-data |
【1.2.2】默认配置
- 默认配置中,三种解析器均被配置,所以当我们使用任意一种编码格式均可以被解析为
request.data
【1.2.3】局部使用
- 当我们想要指定某个视图的请求只可以使用某种编码格式传输,就可以指定
# 导入模块
from rest_framework.parsers import JSONParser, FormParser
class TaskView(APIView):
# 当我们指定使用的解析器 # 请求将只能正确的解析出json和urlencoded编码格式的请求
parser_classes = [JSONParser, FormParser]
def get(self, request):
ser = TaskSer(Task.objects.all(), many=True)
return Response(ser.data)
【1.2.4】全局使用
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
]
}
【2】响应
【2.1】Response对象
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
参数:
- data:要返回的数据。默认为 None。
- status:响应状态码。默认为 None。
- template_name:模板名称,已弃用。改用 data 参数。默认为 None。
- headers:响应头信息。默认为 None。
- exception:是否为异常响应。默认为 False。
- content_type:响应内容类型。默认为 None。
【2.1.1】常用属性
.data
- 传给response对象的序列化后,但尚未render处理的数据
.status_code
- 状态码的数字
.content
- 经过render处理后的响应数据
【2.2】Renderer 渲染器
- REST framework提供了
Renderer
渲染器,用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应格式。 - 如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式
【2.2.1】两种渲染器模块
模块 | 描述 |
---|---|
JSONOpenAPIRenderer | 用于将 API 数据渲染为 OpenAPI(前身为 Swagger)规范的 JSON 格式 |
BrowsableAPIRenderer | 用于将 API 数据渲染为可浏览的 HTML 页面。它为 API 提供了一个交互式的界面,用户可以在浏览器中直观地浏览和测试 API |
【2.2.2】默认配置
-
默认配置中,两种渲染器都被配置了,所以我们可以在浏览器和postman中看到不同的界面
-
【注】当注册了
rest_framework
后,前端界面将会被美化 -
当只设置json渲染器进行渲染时,drf将使用 JSON 渲染器来渲染所有 API 响应,而不会使用任何模板来生成 HTML 页面。因此,在浏览器上就不会显示页面,而是直接显示 JSON 格式的数据。
【2.2.3】局部使用
- 当我们想要指定某个视图的响应只使用
JsonRender
渲染
from rest_framework.renderers import JSONRenderer
class TaskView(APIView):
# 只使用JsonRender渲染响应数据
renderer_classes = [JSONRenderer]
def get(self, request):
ser = TaskSer(Task.objects.all(), many=True)
return Response(ser.data)
【2.2.4】全局使用
# settings.py
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
]
}
【2.3】响应状态码
- 为了方便设置状态码,REST framewrok在
rest_framework.status
模块中提供了常用状态码常量。
【2.3.1】信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
【2.3.2】成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
【2.3.3】重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
【2.3.4】客户端错误 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
【2.3.5】服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
【四】视图组件
【1】两个视图基类
【1.1】APIView
- 请在【View】中查看详细
【1.2】GenericAPIView
【1.2.1】常用类属性
-
基本设置:
-
以下属性控制着基本视图的行为。
-
queryset
- 用于从视图返回对象的查询结果集。
- 通常,你必须设置此属性或者重写
get_queryset()
方法。 - 如果你重写了一个视图的方法,重要的是你应该调用
get_queryset()
方法而不是直接访问该属性,因为queryset
将被计算一次,这些结果将为后续请求缓存起来。
-
serializer_class
- 用于验证和反序列化输入以及用于序列化输出的Serializer类。
- 通常,你必须设置此属性或者重写
get_serializer_class()
方法。
-
lookup_field
- 用于执行各个model实例的对象查找的model字段。默认为
'pk'
。 - 请注意,在使用超链接API时,如果需要使用自定义的值,你需要确保在API视图和序列化类都设置查找字段。
- 用于执行各个model实例的对象查找的model字段。默认为
-
lookup_url_kwarg
- 应用于对象查找的URL关键字参数。它的 URL conf 应该包括一个与这个值相对应的关键字参数。
- 如果取消设置,默认情况下使用与
lookup_field
相同的值。
-
-
Pagination:
-
以下属性用于在与列表视图一起使用时控制分页。
-
pagination_class
- 当分页列出结果时应使用的分页类。
- 默认值与
DEFAULT_PAGINATION_CLASS
设置的值相同,即'rest_framework.pagination.PageNumberPagination'
。
-
-
Filtering:
filter_backends
- 用于过滤查询集的过滤器后端类的列表。
- 默认值与
DEFAULT_FILTER_BACKENDS
设置的值相同。
【1.2.2】常用方法
-
- 返回列表视图中实用的查询集,该查询集还用作详细视图中的查找基础。
- 默认返回由
queryset
属性指定的查询集。 - 这个方法应该总是被调用而不是直接访问
self.queryset
,因为self.queryset
只会被计算一起,然后这些结果将为后续的请求缓存起来。 - 该方法可能会被重写以提供动态行为,比如返回基于发出请求的用户的结果集。
-
- 返回应用于详细视图的对象实例。
- 默认使用
lookup_field
参数过滤基本的查询集。 - 该方法可以被重写以提供更复杂的行为,例如基于多个 URL 参数的对象查找。
-
-
返回应用于序列化的类。默认为返回
serializer_class
属性的值。 -
可以被重写以提供动态的行为,例如对于读取和写入操作使用不同的序列化器,或者为不同类型的用户提供不同的序列化器。
-
【1.2.3】其他方法
- 你通常并不需要重写以下方法,虽然在你使用
GenericAPIView
编写自定义视图的时候可能会调用它们。get_serializer_context(self)
- 返回包含应该提供给序列化程序的任何额外上下文的字典。默认包含
'request'
,'view'
和'format'
这些keys。.
- 返回包含应该提供给序列化程序的任何额外上下文的字典。默认包含
get_serializer(self, instance=None, data=None, many=False, partial=False)
- 返回一个序列化器的实例。
get_paginated_response(self, data)
- 返回分页样式的
Response
对象。
- 返回分页样式的
paginate_queryset(self, queryset)
- 如果需要分页查询,返回页面对象,如果没有为此视图配置分页,则返回
None
。
- 如果需要分页查询,返回页面对象,如果没有为此视图配置分页,则返回
filter_queryset(self, queryset)
- 给定查询集,使用任何过滤器后端进行过滤,返回一个新的查询集。
【2】五个视图扩展类
- Mixin 类提供用于提供基本视图行为的操作。
- 注意mixin类提供动作方法,而不是直接定义处理程序方法,例如
.get()
和.post()
, 这允许更灵活的行为组成。 - Mixin 类可以从
rest_framework.mixins
导入。
视图扩展类 | 功能 | 方法 |
---|---|---|
ListModelMixin | 如果查询集被填充了数据,则返回 200 OK 响应,将查询集的序列化表示作为响应的主体。相应数据可以任意分页。 | 提供一个 .list(request, *args, **kwargs) 方法,实现列出结果集。 |
CreateModelMixin | 如果创建了一个对象,这将返回一个 201 Created 响应,将该对象的序列化表示作为响应的主体。如果序列化的表示中包含名为 url 的键,则响应的 Location 头将填充该值。如果为创建对象提供的请求数据无效,将返回 400 Bad Request ,其中错误详细信息作为响应的正文。 | 提供 .create(request, *args, **kwargs) 方法,实现创建和保存一个新的model实例 |
RetrieveModelMixin | 如果可以检索对象,则返回 200 OK 响应,将该对象的序列化表示作为响应的主体。否则将返回 404 Not Found 。 | 提供一个 .retrieve(request, *args, **kwargs) 方法,实现返回响应中现有模型的实例。 |
UpdateModelMixin | 如果一个对象被更新,这将返回一个 200 OK 响应,将对象的序列化表示作为响应的主体。如果为更新对象提供的请求数据无效,将返回一个 400 Bad Request 响应,错误详细信息作为响应的正文。 | 提供 .update(request, *args, **kwargs) 方法,实现更新和保存现有模型实例。同时还提供了一个 .partial_update(request, *args, **kwargs) 方法,这个方法和 update 方法类似,但更新的所有字段都是可选的。这允许支持 HTTP PATCH 请求。 |
DestroyModelMixin | 如果删除对象,则返回 204 No Content 响应,否则返回 404 Not Found 。 | 提供一个 .destroy(request, *args, **kwargs) 方法,实现删除现有模型实例 |
【3】九个视图子类
- Generic views - Django REST framework中文站点 (q1mi.github.io)
- 这些视图子类遵循了通用的设计原则,封装了常见的 API 操作,使得开发人员能够以更高效、更简洁的方式编写 API 视图。
- 同时,它们也提供了灵活性,允许开发人员通过继承和混合来自定义和扩展功能,以满足特定的业务需求。
- 这些视图类可以从
rest_framework.generics
导入
视图子类 | 功能 | 继承 | 方法 |
---|---|---|---|
CreateAPIView | 用于 仅创建 端点。 | GenericAPIView CreateModelMixin | post |
ListAPIView | 用于 只读 端点以表示模型实例集合 。 | GenericAPIView ListModelMixin | get |
RetrieveAPIView | 用于只读 端点以表示单个模型实例。 | GenericAPIView RetrieveModelMixin | get |
DestroyAPIView | 用于只删除端点以表示单个模型实例。 | GenericAPIView, DestroyModelMixin | delete |
UpdateAPIView | 用于只更新端点以表示单个模型实例 | GenericAPIView UpdateModelMixin | put / patch |
ListCreateAPIView | 用于读写端点以表示模型实例的集合 | GenericAPIView ListModelMixin CreateModelMixin | get / post |
RetrieveUpdateAPIView | 用于 读取或更新 端点以表示 单个模型实例 | GenericAPIView RetrieveModelMixin UpdateModelMixin | get / put /patch |
RetrieveDestroyAPIView | 用于 读取或删除 端点以表示 单个模型实例 | GenericAPIView RetrieveModelMixin DestroyModelMixin | get / delete |
RetrieveUpdateDestroyAPIView | 用于 读写删除 端点以表示 单个模型实例。 | GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin | get /put /patch /delete |
【4】视图集
- DRF允许你将一组相关视图的逻辑组合在单个类(称为
ViewSet
)中。 - 在其他框架中,你也可以找到概念上类似于 ‘Resources’ 或 'Controllers’的类似实现。
ViewSet
只是一种基于类的视图,它不提供任何方法处理程序(如.get()
或.post()
),而是提供诸如.list()
和.create()
之类的操作。ViewSet
的方法处理程序仅使用.as_view()
方法绑定到完成视图的相应操作。- 通常不是在 urlconf 中的视图集中显示注册视图,而是要使用路由类注册视图集,该类会自动为你确定 urlconf。
【4.1】ViewSetMixin
- 重写了
.as_view()
方法,使其接受一个actions
关键字参数,用于将 HTTP 方法与资源的动作进行绑定- 【注】所有继承了ViewSetMixin的视图类,在路由中都需要传递actions参数
- actions参数通常为字典形式
视图类.as_view({'请求方式':'执行的方法名'})
- 如:
MyViewSet.as_view({'get': 'list', 'post': 'create'})
- 如:
- 使用时,注意继承ViewSetMixin时需要在最左侧
class CommonView(ViewSetMixin,ListAPIView)
# 凡是继承了 ViewSetMixin 的视图类
# 路由写法都需要改变
######## urls.py ########
urlpatterns = [
path('xxx/',xxx.as_view({'请求方法名':'对应的视图函数'}))
]
- 详细原理请看【DRF源码分析】
【示例】
# urls.py
from .views import CommonView
from django.urls import path
urlpatterns = [
path('no_router/', CommonView.as_view(actions={'get': 'xxx', 'post': 'abc'}))
]
# views.py
class CommonView(ViewSetMixin,APIView):
def xxx(self, request):
return Response('xxx')
def abc(self, request):
return Response('abc')
【4.2】四种视图集
视图集 | 功能 | 继承 | 方法 |
---|---|---|---|
ViewSet | ViewSet 继承自 APIView 。你可以使用任何标准属性,如 permission_classes , authentication_classes 以便控制视图集上的 API 策略。 | ViewSetMixin, views.APIView | ViewSet 类不提供任何操作的实现。为了使用 ViewSet 类,你将重写该类并显式地定义动作实现。 |
GenericViewSet | GenericViewSet 类继承自 GenericAPIView,并提供了 get_object, get_queryset 方法和其他通用视图基本行为的默认配置,但默认情况不包括任何操作。 | ViewSetMixin, generics.GenericAPIView | 为了使用 GenericViewSet 类,你需要覆盖该类并且要么混合所需的混合类,要么明确定义动作的实现。 |
ModelViewSet | ModelViewSet 类继承自 GenericAPIView,并通过混合各种混合类的行为来实现各种动作 | mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet | ModelViewSet 类提供的动作包括 .list() 、.retrieve() 、.create() 、.update() 、.partial_update() 和 .destroy() 。 |
ReadOnlyModelViewSet | ReadOnlyModelViewSet 类也继承自 GenericAPIView。 | mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet | 与 ModelViewSet 类类似,它也包括各种动作的实现,但与 ModelViewSet 不同的是,它只提供了“只读”动作,即 .list() 和 .retrieve() |
【五】路由组件
资源路由允许你快速声明给定的有足够控制器的所有公共路由。而不是为你的index…声明单独的路由,一个强大的路由能在一行代码中声明它们。
— Ruby on Rails 文档
【1】使用SimpleRouter快速生成路由
from .views import TaskViewV3
# 导入模块
from rest_framework.routers import SimpleRouter
from django.urls import path, include
# 实例化得到对象
router = SimpleRouter()
# 执行对象的register方法
router.register(prefix='task', viewset=TaskViewV3, basename='task')
urlpatterns = [
# 获取对象的urls属性
path('v1/', include(router.urls))
]
【1.1】参数
-
register()
方法有两个强制参数:-
prefix
- 用于此组路由的URL前缀。 -
viewset
- 处理请求的viewset类。
-
-
还可以指定一个附加参数(可选):
base_name
- 用于创建的URL名称的基本名称。- 如果不设置该参数,将根据视图集的
queryset
属性(如果有)来自动生成基本名称。 - 注意,如果视图集不包括
queryset
属性,那么在注册视图集时必须设置base_name
。
【1.2】在路由中将快速生成的路由添加到urlpatterns
的多种方式
- 路由器实例上的
.urls
属性只是一个URL模式的标准列表。对于如何添加这些URL,有很多不同的写法
【1.2.1】将router.urls
附加到现有视图的列表中 urlpatterns += router.urls
urlpatterns = [
path('',根路由)
]
urlpatterns += router.urls
【1.2.2】使用Django的include
函数
urlpatterns = [
path('v1/', include(router.urls))
]
【2】SimpleRouter
- 该路由器包括标准集合
list
,create
,retrieve
,update
,partial_update
和destroy
动作的路由。 - 视图集中还可以使用
@ detail_route
或@ list_route
装饰器标记要被路由的其他方法。
-
默认情况下,由
SimpleRouter
创建的URL将附加尾部斜杠。 在实例化路由器时,可以通过将trailing_slash
参数设置为`False’来修改此行为。router = SimpleRouter(trailing_slash=False)
-
路由器将匹配包含除斜杠和句点字符以外的任何字符的查找值。
-
对于更严格(或更宽松)的查找模式,请在视图集上设置
lookup_value_regex
属性。- 例如,你可以将查找限制为有效的UUID:
class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
lookup_field = 'my_model_id'
lookup_value_regex = '[0-9a-f]{32}'
【3】DefaultRouter
- 这个路由器类似于上面的
SimpleRouter
,但是还包括一个默认返回所有列表视图的超链接的API根视图。它还生成可选的.json
样式格式后缀的路由。
- 与
SimpleRouter
一样,在实例化路由器时,可以通过将trailing_slash
参数设置为False
来删除URL路由的尾部斜杠
- 上述是访问路由根路径时,使用DefaultRouter将会自动生成根路径的页面,如果使用SimpleRouter将会报错
【4】action装饰器
from rest_framework.decorators import action
@action
装饰器函数是Django REST Framework中用于定义自定义动作的一种方法。- 它可以让你在视图集中添加额外的自定义动作,并将它们映射到特定的HTTP方法和URL路径上。
【4.1】使用场景
@action
装饰器的使用场景包括但不限于以下几种情况:
- 自定义操作:当你需要在视图集中添加一些不属于标准CRUD操作(创建、读取、更新、删除)的自定义操作时,可以使用
@action
装饰器。例如,向用户发送邮件、将资源标记为喜欢或收藏等。 - 扩展标准操作:有时,标准的CRUD操作可能不足以满足你的需求,你可能需要为某些资源添加额外的操作。使用
@action
装饰器可以轻松地在视图集中添加这些自定义操作。 - 复杂业务逻辑:某些操作可能涉及到复杂的业务逻辑,无法简单地通过标准的CRUD操作来实现。在这种情况下,你可以使用
@action
装饰器来定义并实现这些操作。 - 对单个实例或整个集合的操作:通过
detail
参数,你可以指定自定义操作是针对单个实例还是整个集合的。这样可以灵活地定义不同类型的操作。 - 扩展API的功能:使用
@action
装饰器可以轻松地扩展API的功能,使其更加灵活和强大,从而满足不同场景下的需求。
【4.2】使用
action(methods=None, detail=None, url_path=None, url_name=None, **kwargs)
源码说明:
将一个ViewSet方法标记为可路由的动作。
被
@action
装饰的函数将被赋予一个mapping
属性,一个MethodMapper
,可以用来在路由动作上添加额外的基于方法的行为。
- 参数说明
methods
:一个列表,表示此动作响应的HTTP方法名称。默认为仅接受GET请求。detail
:必填项。确定此动作是应用于实例/详情请求还是集合/列表请求。url_path
:定义此动作的URL片段。默认为被装饰方法的名称。url_name
:定义此动作的内部(reverse
)URL名称。默认为被装饰方法的名称,其中下划线被连字符替换。kwargs
:用于在视图上设置其他属性。这可用于覆盖视图集级别的*_classes
设置,类似于函数式API视图中的@renderer_classes
等装饰器的工作原理
【4.3】使用案例(自动生成路由)
- 当您使用自动生成的路由时,Django REST Framework会自动检测装饰有
@action
的方法,并将其转换为相应的URL路径。 - 这是因为
@action
装饰器会影响ViewSet
中的路由配置,并告诉框架应该将这些自定义操作公开为API端点。
# views.py
class TaskViewV3(GenericViewSet):
queryset = Task.objects.all()
serializer_class = TaskSer
@action(methods=['POST'], detail=False)
def login(self, request):
'''登录代码逻辑'''
return Response('xxx')
@action(methods=['POST'], detail=False)
def register(self, request):
'''注册代码逻辑'''
return Response('xxx')
@action(methods=['PUT', 'DELETE', 'PATCH'], detail=False)
def other(self, request, *args, **kwargs):
'''其他请求方式的操作逻辑'''
return Response('xxx')
# urls.py
from .views import TaskViewV3
from rest_framework.routers import SimpleRouter
from django.urls import path, include
router = SimpleRouter()
router.register(prefix='task', viewset=TaskViewV3, basename='task')
urlpatterns = [
path('v1/', include(router.urls))
]
【4.3.1】使用自动生成路由 + action的url_path
参数 实现自定义路径
# views.py
class CommonView(ViewSet):
@action(methods=['get'], detail=False, url_path='xxx', url_name='xxx')
def xxx(self, request):
return Response('xxx')
@action(methods=['post'], detail=False, url_path='abc', url_name='abc')
def abc(self, request):
return Response('abc')
# urls.py
router = SimpleRouter()
router.register(prefix='router', viewset=CommonView, basename='common')
urlpatterns = [
# 此处留空
]
urlpatterns += router.urls
【4.4】使用案例2(不使用自动生成路由的情况下使用aciton装饰器)
@action
装饰器的作用在于为自动生成路由的情况下提供便利。但即使在手动定义路由的情况下,@action
装饰器仍然有几个作用:- 提供附加的元数据: 使用
@action
装饰器可以为自定义操作提供附加的元数据,例如detail
参数用于指定操作是针对单个对象还是整个集合的。 - 提高可读性: 在视图类中使用
@action
装饰器标记自定义操作,可以使代码更具可读性和清晰度,因为它明确指示了这是一个自定义操作。 - 集中定义自定义操作: 使用
@action
装饰器将自定义操作直接与视图类关联,使得在代码中更容易找到和理解视图类的功能。
- 提供附加的元数据: 使用
# urls.py
from .views import CommonView
from django.urls import path
urlpatterns = [
path('no_router/', CommonView.as_view(actions={'get': 'xxx', 'post': 'abc'}))
]
class CommonView(ViewSet):
@action(methods=['get'], detail=False)
def xxx(self, request):
return Response('xxx')
@action(methods=['post'], detail=False)
def abc(self, request):
return Response('abc')
自动生成路由很方便,建议使用自动生成