源自b站 BV1Sz4y1o7E8 的教学所作笔记
1.起始
DRF全称DjangoRestFramework,用于快速构建 Web RESTful API
1- 使用pip install djangorestframework下载DRF
2- 注册子应用以使用DRF
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # DRF应用
]
2.初步体验DRF
首先定义models.py中的BookInfo模型类
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期')
bread = models.IntegerField(default=0, verbose_name='阅读量')
bcomment = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_books' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
def __str__(self):
"""定义每个数据对象的显示信息"""
return self.btitle
第一步是构建序列化器
选择需要的子应用目录下新建py文件serializers.py
from rest_framework import serializers
from .models import BookInfo
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo # 指定序列化从哪个模型映射字段
fields = "__all__" # 映射哪些字段,此处选择全部字段
第二步是创建类视图
在子应用下在views.py创建类
class BookInfoView(ModelViewSet):
""" 定义类视图 """
# 指定查询集
queryset = BookInfo.objects.all()
# 指定序列化器
serializer_class = BookInfoSerializer
第三步是创建路由
在子应用下的urls.py文件中添加路由
from django.urls import re_path
from rest_framework.routers import DefaultRouter #需要引入DefaultRouter
from . import views
app_name = 'booktest'
urlpatterns = [
...
]
router = DefaultRouter() # 创建路由器
router.register(r'restbooks',views.BookInfoView) # 注册路由
urlpatterns += router.urls # 把生成好的代码拼接到 urlpatterns 中
第四步在浏览器中访问指定路径即可
3.序列化
3.1 创建模型
前提所使用的模型对象,即BookInfo
# models.py
from django.db import models
#定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期')
bread = models.IntegerField(default=0, verbose_name='阅读量')
bcomment = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_books' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
def __str__(self):
"""定义每个数据对象的显示信息"""
return self.btitle
其中数据表有四个数据:
第二个模型对象为HeroInfo
#定义英雄模型类HeroInfo
class HeroInfo(models.Model):
GENDER_CHOICES = (
(0, 'female'),
(1, 'male')
)
hname = models.CharField(max_length=20, verbose_name='名称')
hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_heros'
verbose_name = '英雄'
verbose_name_plural = verbose_name
def __str__(self):
return self.hname
其表中有多个数据:
3.2 创建序列化器
根据模型对象BookInfo,创建对应的序列化器。需要在应用目录下新建一个py文件,这里将其命名为serializers.py,创建的序列化器类为BookInfoSerializer
from rest_framework import serializers
from .models import BookInfo
class BookInfoSerializer(serializers.Serializer):
"""
参数
lable 即运行完后在浏览器界面的DRF框架表单中显示的名称值
read_only 即只读,不可更改,因此只做输出不做输入
requires 即是否必填,默认为 required=True ,
"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=True)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
3.3 在shell显示效果
在例子中使用Djago shell功能,关于Django shell详细使用方法可看Django shell 使用方法
另外可以使用ipython作为辅助,使用pip install ipython命令进行下载
准备工作
使用python manage.py shell进入终端命令行,在shell命令行中,引入所需的类
案例一 - 序列化多个
查所有:
序列化结果:
[OrderedDict([('id', 1), ('btitle', '射雕英雄传'), ('bpub_date', '1980-05-01'), ('bread', 12), ('bcomment', 34)]), OrderedDict([('id', 2), ('btitle', '天龙八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40)]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20),('bcomment', 80)]), OrderedDict([('id', 4), ('btitle', '雪山飞狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24)])]
本例中,当查询到的是多个对象时,在实例化序列化器的时候参数需要加上many=True,若是使用book = BookInfo.objects.get(id = 1)则不用加上此参数
案例二 - 自定义添加序列化字段
退出shell命令行,更改序列化器中的内容,在【3.1 创建序列化器】中的序列化器类中添加一条字段
# 由于添加了一条字段,改变了序列化器,因此需要重启shell
hello =serializers.CharField(label='你好',required=False)
在使用的时候给BookInfo的实例化类中添加hello属性,可以将hello属性进行序列化
序列化结果
{'id': 1, 'btitle': '射雕英雄传', 'bpub_date': '1980-05-01', 'bread': 12, 'bcomment': 34, 'hello': '11'}
案例三 - 关联序列化(多对一)
注意BookInfo对应的表为主表,HeroInfo对应的表为从表,HeroInfo的hbook为外键 在【3.1 创建序列化器】再添加一个序列化器类
class HeroInfoSerializer(serializers.Serializer):
"""英雄数据序列化器"""
GENDER_CHOICES = (
(0, 'female'),
(1, 'male')
)
id = serializers.IntegerField(label='ID', read_only=True)
hname = serializers.CharField(label='名字', max_length=20)
hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性别', required=False)
hcomment = serializers.CharField(label='描述信息', max_length=200, required=False, allow_null=True)
"""
写法一:默认将关联模型的 id 进行序列化
hbook = serializers.PrimaryKeyRelatedField(label='书籍', read_only=True)
"""
"""
写法二:默认是将关联模型的 __str__ 方法返回值序列化出来
hbook = serializers.StringRelatedField(label='书籍',read_only=True)
"""
"""
写法三:如果是这种写法,意味着反序列化时如果赋予的 hbook 不是 BookInfo 里面已经存在的 id 就会报错
hbook = serializers.PrimaryKeyRelatedField(label='书籍', queryset=BookInfo.objects.all())
"""
"""
写法四:BookInfoSerializer的实例化,会将关联模型对象的序列化器中所有的字段全部序列化出来
hbook = BookInfoSerializer()
"""
写法一:过程与结果
写法二:过程与结果
写法三:不予展示
写法四:过程与结果
案例四 - 关联序列化(一对多)
注意BookInfo对应的表为主表,HeroInfo对应的表为从表,HeroInfo的hbook为外键
写法一:
在序列化器类BookInfoSerializer多添加一个字段
""" 写法规则是 关联的模型类名小_set"""
# 注意添加上去一个 many = True
heroinfo_set = HeroInfoSerializer(many = True)
过程与结果如下:
{'id': 1, 'btitle': '射雕英雄传', 'bpub_date': '1980-05-01', 'bread': 12, 'bcomment': 34, 'hello': '11', 'heroinfo_set': [OrderedDict([('id', 1), ('hname', '郭靖'), ('hgender', 1), ('hcomment', '降龙十八掌')]), OrderedDict([('id', 2), ('hname', '黄蓉'), ('hgender', 0), ('hcomment', '打狗棍法')]), OrderedDict([('id',3), ('hname', '黄药师'), ('hgender', 1), ('hcomment', '弹指神通')]), OrderedDict([('id', 4), ('hname', '欧阳锋'), ('hgender', 1), ('hcomment', '蛤蟆功')]), OrderedDict([('id', 5), ('hname', '梅超风'), ('hgender', 0), ('hcomment', '九阴白骨爪')]), OrderedDict([('id', 18), ('hname', '张三'), ('hgender', 0), ('hcomment', None)])]}
写法二:
# 注意添加上去一个 many = True
heroinfo_set = serializers.StringRelatedField(many=True)
# 或 hero_set = serializers.PrimaryKeyRelatedField(many=True)
结果如下:
{'id': 1, 'btitle': '射雕英雄传', 'bpub_date': '1980-05-01', 'bread': 12, 'bcomment': 34, 'hello': '11', 'heroinfo_set': ['郭靖', '黄蓉', '黄药师', '欧阳锋', '梅超风', '张三']}
4.反序列化
4.1 反序列化之添加
同上面的操作,在shell命令行中进行实验
1.导入需要的模块
from booktest.models import BookInfo,HeroInfo
from booktest.serializers import BookInfoSerializer,HeroInfoSerializer
2.首先给定一组数据(自定义一组前端传过来的json数据),名为data
data = {
'btitle':'星三国',
'bpub_date':'1991-3-15'
}
3.实例化,并且需要传入参数data
serializers = BookInfoSerializer(data=data)
4.进行验证 (失败或成功)
"""
在获取反序列化的数据前,必须调用 is_valid() 方法进行验证,
验证成功返回True,否则返回False。
"""
serializers.is_valid(raise_exception=True)
"""
验证失败,可以通过序列化器对象的errors属性获取错误信息,
返回字典,包含了字段和字段的错误。如果是非字段错误,
可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。
"""
# serializer.errors # 验证失败
""" 验证成功,可以通过序列化器对象的**validated_data**属性获取数据。 """
# serializer.validated_data # 验证成功
5.进行保存
"""
本案例是添加操作,则会调用序列化器的create方法,
如果是更新操作,则会调用序列化器的update方法
"""
serializers.save()
4.2 反序列化之更新
在本例中使用新的data以更新上一例中的添加操作
1.使用新的数据data
data = {
'btitle':'超级三国',
'bpub_date':'2003-10-15'
}
2.获取数据库中的某个对象 book
book = BookInfo.objects.get(id = 18)
3.实例化序列化器
"""
假如参数中有instance,则表明保存时调用序列化器的update函数
反序列化id为18的书籍对象
"""
serializer = BookInfoSerializer(instance=book,data=data)
4.验证
serializer.is_valid(raise_exception=True)
5.保存
"""
本案例是更新操作,则会调用序列化器的update方法,
如果是添加操作,则会调用序列化器的create方法
"""
serializer.save()
4.3 序列化器的create,update方法
以BookInfoSerializer为例
from rest_framework import serializers
from .models import BookInfo
class BookInfoSerializer(serializers.Serializer):
# 字段代码,忽略不看
# ... ...
"""
应该注意到,在4.1 中的实例化操作中,有以下代码:
serializers = BookInfoSerializer(data=data)
说明传入的data此时作为create函数中的validated_data参数,为字典格式
"""
def create(self, validated_data):
"""新建"""
# 使用 模型类.objects.create方法进行添加,
# 由于是字典格式,因此作为参数时前面要加**
return BookInfo.objects.create(**validated_data)
"""
在4.2中的实例化操作中,有:
serializer = BookInfoSerializer(instance=book,data=data)
说明传入的book作为update的instance参数,传入的data作为validated_data参数
"""
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
instance.save()
return instance
5.ModelSerializer
在上面的例子中,序列化器用的都是serializers中的Serializer类,但是使用ModelSerializer0会更加方便。在【2.初步体验DRF】中有使用
其优点在于:
1.ModelSerializer可以根据模型自动生成序列化器中的字段
2.ModelSerializer已经实现了create以及update方法
但是有的时候需要重写其中的create方法,比如说有的时候前端需要填写双重密码校验,这样可能会传过来类似{'password1':'123456','password2':'123456'}的数据,可是password2是校验密码,因此ModelSerializers中的create方法需要重写以删除password2
例子
序列化器:
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo # 指定序列化从哪个模型映射字段
fields = "__all__" # 映射哪些字段,此处选择全部字段
# fields = ['id','btitle','bpub_date'] # 映射id,btitle,bpub_date三个字段
# exclude = ['bpub_date'] # 除了bpub_date不需要映射其他都要
在shell命令行运行查看:
因此映射完成后的字段是:
id = IntegerField(label='ID', read_only=True)
btitle = CharField(label='名称', max_length=20)
bpub_date = DateField(label='发布日期')
bread = IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)
is_delete = BooleanField(label='逻辑删除', required=False)
新增字段
由于ModelSerializer是Serializer的子类,因此也可以进行新增字段,假如新增good
class BookInfoSerializer(serializers.ModelSerializer):
good = serializers.CharField(label='好评度', max_length=20)
class Meta:
model = BookInfo # 指定序列化从哪个模型映射字段
fields = "__all__"
# exclude = ['bpub_date']
如果使用列表形式,那么记得加上新增的good
fields=['id','btitle','bpub_date','good']
修改选项参数
假如说对映射完后的bread字段的中min_value不满意,想将其变为0,对required字段不满意,想改为 True ,对那么可以:
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo # 指定序列化从哪个模型映射字段
fields = "__all__"
# 修改选项参数
extra_kwargs = {
'bread':{'min_value':0,'required':True}, # 将bread中的min_value改为0,required改为True
'bcomment':{'write_only':True} #将bcomment改为只做反序列化(只能写进)
}
# 若想改变只读选项,则需要再另外定义
read_only_fields = ['bread'] # 将bread改为只读
最终的bread以及bcomment结果:
# 注意,此时的bread中有read_only,因此自动取消掉requied选项
bread = IntegerField(label='阅读量', min_value=0, read_only=True)
bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False, write_only=True)
6.视图
6.1 Request和Response
Request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
常用属性
1. data
request.data 返回解析之后的请求体数据。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求方式解析后的数据
- 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2. query_params
request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。
Response
rest_framework.response.Response
REST framework提供了一个响应类
Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。REST framework提供了
Renderer渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
构造方式
Response(data, status=None, template_name=None, headers=None, content_type=None)
data数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer渲染器处理data。
data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer序列化器序列化处理后(转为了Python字典类型)再传递给data参数。
参数说明:
data: 为响应准备的序列化处理后的数据;status: 状态码,默认200;template_name: 模板名称,如果使用HTMLRenderer时需指明;headers: 用于存放响应头信息的字典;content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。
常用属性:
data
传给response对象的序列化后,但尚未render处理的数据
status_code
状态码的数字
content
经过render处理后的响应数据
6.2 状态码
详见 【DRF】常见状态码
6.3 DRF中的视图
在此使用【3.序列化】中的
BookInfo作为案例表
APIView 以及案例
首先创建序列化器
创建一个新的序列化器BookInfoModelSerialize
class BookInfoModelSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = "__all__"
两个视图类的路由
re_path(r"^apibooks/$",views.BookListAPIView.as_view(),name = "BookListAPIView"),
re_path(r"^apibooks/(?P<id>\d+)/$",views.BookDetailAPIView.as_view(),name = "BookDetailAPIView")
类视图1:查询以及新增
创建一个类视图 BookListAPIView
from .models import BookInfo
""" APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
这个类会在后面介绍 """
from rest_framework.views import APIView,status
from .serializers import BookInfoModelSerializer
""" 引入DRF中的Response代替HttpResponse,JsonResponse等对象作为返回的对象
它可以将数据内容转换(render渲染)成符合前端需求的类型。"""
from rest_framework.response import Response
class BookListAPIView(APIView):
# 查所有书籍
def get(self,request):
# 获取所有书籍
books = BookInfo.objects.all()
# 进行序列化
serializer = BookInfoModelSerializer(instance=books,many=True)
""" 本来 serializer.data 的数据类型为OrderedDict(或由OrderedDict形成的列表)
现在使用Response会将数据渲染成json数据并返回给前端 """
return Response(serializer.data)
#新增书籍
def post(self,request):
""" 当视图类继承DRF中的APIView时,其中的request成为DRF提供的扩展了
HttpRequest类的Request类的对象
其中常用的属性为request.data以及request.query_params
这里通过加强的request.data获取解析之后的数据 """
data = request.data
serializer = BookInfoModelSerializer(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
类视图2:单一查询/修改/删除
创建类视图BookDetailAPIView
from .models import BookInfo
from rest_framework.views import APIView,status
from .serializers import BookInfoModelSerializer
class BookDetailAPIView(APIView):
# 查单一书籍
def get(self,request,id):
try:
book = BookInfo.objects.get(id = id)
except BookInfo.DoesNotExist:
""" 使用DRF中的status
这里指定为404 """
return Response(status = status.HTTP_404_NOT_FOUND)
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
# 修改单一书籍
def put(self,request,id):
try:
book = BookInfo.objects.get(id = id)
except BookInfo.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)
serializer = BookInfoModelSerializer(instance=book,data=request.data)
print(request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(request.data)
# 删除单一书籍
def delete(self, instance,id):
try:
book = BookInfo.objects.get(id = id)
except BookInfo.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)
book.delete()
return Response(status = status.HTTP_204_NO_CONTENT)
GenericAPIView 以及案例
相比较APIView,GenericAPIView中可以赋予序列化器和查询集到"变量"中,相比较起来更加灵活
另外使用 GenericAPIView 查询单一书籍,须使用到 get_object(),其作用在于通过前端路由传过来的pk值获取对应id的书本对象,需要注意的是其路由参数的值应为 pk
# urls.py
""" (?P<pk>\d+) 中应为pk """
re_path(r"^genericbooks/(?P<pk>\d+)/$",views.BookDetailGenericView.as_view(),name = "BookDetailGenericView")
查询/新增 以及 单一查询/修改/删除
# views.py
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.views import status
class BookListGenericView(GenericAPIView):
""" 指定的序列化器 """
serializer_class = BookInfoModelSerializer
""" 指定的查询集 """
queryset = BookInfo.objects.all()
""" 1.查询 """
def get(self,request):
""" 通过 GenericAPIView 中的 get_queryset() 方法 获取 指定的查询集"""
books = self.get_queryset()
""" 通过 GenericAPIView 中的 get_serializer 方法 获取 指定的序列化器"""
serializer = self.get_serializer(instance = books,many = True)
return Response(serializer.data)
""" 2.新增 """
def post(self,request):
data = request.data
serializer = self.get_serializer(data = data)
serializer.is_valid(raise_exception = True)
serializer.save()
return Response(serializer.data)
class BookDetailGenericView(GenericAPIView):
""" 指定的查询集 """
queryset = BookInfo.objects.all()
""" 指定的序列化器 """
serializer_class = BookInfoModelSerializer
""" 1.单一查询 """
def get(self, request, pk):
""" get_object()方法根据pk参数查找queryset中的数据对象 """
book = self.get_object()
serializer = self.get_serializer(book)
return Response(serializer.data)
""" 2.单一修改 """
def put(self,request,pk):
book = self.get_object()
serializer = self.get_serializer(instance = book,data = request.data)
serializer.is_valid(raise_exception = True)
serializer.save()
return Response(serializer.data)
""" 3.单一删除 """
def delete(self,request,pk):
book = self.get_object()
book.delete()
return Response(status = status.HTTP_204_NO_CONTENT)
五个拓展类 Mixin
五个拓展类分别为
ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,可以简略地写增删改查等操作
通过引入五个拓展类来缩短【GenericAPIView 以及案例】中的代码
from rest_framework.views import APIView,status
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
""" 引入五个拓展类 """
from rest_framework.mixins import
ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
""" 拓展类 ListModelMixin,CreateModelMixin 与GenericAPIView一同作为父类被继承 """
class BookListGenericView(GenericAPIView,ListModelMixin,CreateModelMixin):
# 指定序列化器
serializer_class = BookInfoModelSerializer
# 指定查询集
queryset = BookInfo.objects.all()
""" 查询 """
def get(self,request):
return self.list(request) # 使用 ListModelMixin 中的 list 方法
""" 新增 """
def post(self,request):
return self.create(request) # 使用 CreateModelMixin 中的 create 方法
""" 拓展类 RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin 与GenericAPIView一同作为父类被继承 """
class BookDetailGenericView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
""" 单一查询 """
def get(self, request, pk):
return self.retrieve(request) # 使用 RetrieveModelMixin 中的 retrieve 方法
""" 单一修改 """
def put(self,request,pk):
return self.update(request) # 使用 UpdateModelMixin 中的 update 方法
""" 单一删除 """
def delete(self,request,pk):
return self.destroy(request) # 使用 DestroyModelMixin 中的 destroy 方法
子类视图
CreateAPIView 提供 post 方法
继承自: GenericAPIView、CreateModelMixin
ListAPIView 提供 get 方法
继承自:GenericAPIView、ListModelMixin
RetrieveAPIView 提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
DestroyAPIView 提供 delete 方法
继承自:GenericAPIView、DestroyModelMixin
UpdateAPIView 提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
RetrieveUpdateAPIView 提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
RetrieveUpdateDestroyAPIView 提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin
使用子类视图将更加精简,只需指定序列化器以及查询集即可完成逻辑
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
""" 查询以及新增 """
class BookListGenericView(ListCreateAPIView):
# 指定序列化器
serializer_class = BookInfoModelSerializer
# 指定查询集
queryset = BookInfo.objects.all()
""" 单一查询/修改/删除 """
class BookDetailGenericView(RetrieveUpdateDestroyAPIView):
# 指定序列化器
serializer_class = BookInfoModelSerializer
# 指定查询集
queryset = BookInfo.objects.all()
6.4 使用视图集
视图集作用
- 将所有的接口写在一个类视图中(类视图中的方法不再使用
get,post...而是使用 行为action - 重写了as_view(),将改写的方法包装成字典,例如
as_view({'get':'list'})
各种动作:
list() 提供一组数据
retrieve() 提供单个数据
create() 创建数据
update() 保存数据
destroy() 删除数据
ViewSet
继承自APIView与ViewSetMixin,在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
案例如下,将查询接口以及查询单一写在一个类中:
from rest_framework.viewsets import ViewSet
""" ViewSet 继承 APIView ,因此序列化的方式与APIView的方式一样 """
class BookInfoViewSet(ViewSet):
""" 查询 """
def list(self,request):
try:
books = BookInfo.objects.all()
except BookInfo.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)
serializer = BookInfoModelSerializer(instance=books,many=True)
return Response(serializer.data)
""" 单一查询 """
def retrieve(self,request,pk = None):
try:
book = BookInfo.objects.get(id = pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
路由实现,可见两个路由均使用同一个类BookInfoViewSet,并且传递参数给as_view()
re_path(r"^viewsetbooks/$",views.BookInfoViewSet.as_view({'get':'list'})),
re_path(r"^viewsetbooks/(?P<pk>\d+)/$",views.BookInfoViewSet.as_view({'get':'retrieve'})),
GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destroy等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
实现 查询/单一查询 的案例如下:
from rest_framework.viewsets import GenericViewSet
class
BookInfoGenericViewSet(ListModelMixin,RetrieveModelMixin,GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
"""
不用编写方法就可以实现查询/单一查询
因为视图集本身调用list、retrieve、create等方法,
而ListModelMixin,RetrieveModelMixin自己内置list,retrieve方法,会自动调用
"""
路由实现如下:
re_path(r"^geviewsetbooks/$", views.BookInfoGenericViewSet.as_view({'get': 'list'})),
re_path(r"^geviewsetbooks/(?P<pk>\d+)/$", views.BookInfoGenericViewSet.as_view({'get': 'retrieve'})),
ReadOnlyModelViewSet
另外,查询/单一查询 都是只读的,这样的话可以直接使用ReadOnlyModelViewSet,它继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。那么 查询/单一查询可写成如下:
from rest_framework.viewsets import ReadOnlyModelViewSet
""" ReadOnlyModelViewSet继承了GenericViewSet、ListModelMixin、RetrieveModelMixin """
class BookInfoGenericViewSet(ReadOnlyModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
ModelViewSet
如果想不止拥有查询功能,还想增删改查都有,那么可以使用最终版本的 ModelViewSet
它继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestroyModelMixin。
包含查询/新增,单一查询/修改/删除的视图如下。也可以直接查看【2.初体验DRF】
class BookInfoModelViewSet(ModelViewSet):
""" 定义类视图 """
# 指定查询集
queryset = BookInfo.objects.all()
# 指定序列化器
serializer_class = BookInfoSerializer
路由实现如下:
re_path(r"^geviewsetbooks/$", views.BookInfoModelViewSet.as_view({'get': 'list'})),
re_path(r"^geviewsetbooks/(?P<pk>\d+)/$", views.BookInfoModelViewSet.as_view({'get': 'retrieve'})),
在视图集上新增动作
现在在上面的ModelViewSet实验中新增视图方法
class BookInfoModelViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
# 目前具有的基本增删改查动作之外,还可以自定义另外动作
""" 现在自定义动作 latest ,查出最后一个id的具体数据 """
def latest(self,request):
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(instance = book)
return Response(serializer.data)
""" 现在自定义动作 update_read ,进行单独修改书本的阅读量 """
def update_read(self,request,pk = None):
book = self.get_object()
book.bread = request.data.get('bread')
book.save()
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
自定义动作中的路由
""" ModelViewSet视图集的基本增删改查路由 """
re_path(r"^geviewsetbooks/$", views.BookInfoModelViewSet.as_view({'get': 'list','post':'create'})),
re_path(r"^geviewsetbooks/(?P<pk>\d+)/$", views.BookInfoModelViewSet.as_view({'get': 'retrieve','put':'update','delete':'destroy'})),
# 如果存在 增删改查 之外的额外行为 应该单独定义路由
# 如果此行为不需要pk,那么他就是列表视图,但是列表视图默认只有list,create
""" 在列表视图路由后缀加上latest,使用get方法访问此路由将显示最后一本书 """
re_path(r"^geviewsetbooks/latest$", views.BookInfoModelViewSet.as_view({'get': 'latest'})),
""" 后缀加上update_read,使用put方法访问此路由可以选择修改书本的阅读量 """
re_path(r"^geviewsetbooks/(?P<pk>\d+)/update_read$", views.BookInfoModelViewSet.as_view({'put':'update_read'})),
在视图集上使用路由器
只有在视图集中才能使用路由器
以上面的ModelViewSet新增动作实验为例,添加基本路由器
from rest_framework.decorators import action
class BookInfoModelViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
# 目前具有的基本增删改查动作之外,还可以自定义另外动作
""" 添加装饰器action
methods 表示action对应的请求方式
detail True时表示对应的路由为 xxx/<pk>/action方法名/
False时表示对应的路由为 xxx/action方法名/ """
@action(methods=['get'],detail=False)
def latest(self,request):
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(instance = book)
return Response(serializer.data)
""" """
def update_read(self,request,pk = None):
book = self.get_object()
book.bread = request.data.get('bread')
book.save()
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
在urls.py中添加路由器
from rest_framework.routers import DefaultRouter
urlpatterns = [
...
]
router = DefaultRouter() # 创建路由器
router.register(r"geviewsetbooks",views.BookInfoModelViewSet) # 注册路由
urlpatterns += router.urls # 把生成好的代码拼接到 urlpatterns 中
""" 此时形成的代码如下
^geviewsetbooks/$ name: book-list
^geviewsetbooks/{pk}/$ name: book-detail
另外新增的action的路由看视图中的action装饰器而定 """