Day 23 DRF序列化器高级用法及相关源码
一、修改删除接口
根据restful规范–资源操作由请求方式决定
所以目前我们需要不同不同的路由 和 视图类来实现这个功能
1. 路由的配置
# urls.py
url(r'^book/$', views.Book.as_view()), # get 所有的查询 以及增加
url(r'^book/(?P<id>\d+)$', views.BookInfo.as_view()), # 单个的 删 改 查
2.CBV视图的编写
# views.py
class BookInfo(APIView):
# 查询单本书的操作,与查询多本相同 都是拿到对象后,经过序列器返回
def get(self, request, id):
one_book = models.Book.objects.filter(id=id).first()
if one_book:
ser = serializer.BookSerializer(instance=one_book)
return Response(ser.data)
else:
raise ValidationError('找不到这本书哦!')
# 在这里我们就可以不用通过序列器了,直接返回操作信息就好了
def delete(self, request, id):
one_book = models.Book.objects.filter(id=id)
if one_book:
one_book.delete()
return Response('删除成功!')
else:
raise ValidationError('找不到这本书哦!')
# 这里需要经过序列器 步骤相同
def patch(self, request, id):
result = {'code': 100, 'msg': '修改成功!'}
one_book = models.Book.objects.filter(pk=id)
print(request.data)
# 在实例化 对象的时候 我们也要将修改的数据 data 传进去,partial是否为单个 可以不加
ser = serializer.BookSerializer(data=request.data, instance=one_book, partial=True)
if ser.is_valid():
ser.save() # 这里我们需要注意的是 要调用save方法 我们要在序列器中重写啊update
return Response(result) # 不能返回 ser.data会报错
else:
result['code'] = 101
result['data'] = '该书不存在!'
return Response(result)
3.序列器
def update(self, instance, validated_data): # 会自动补全后面的参数
instance.update(**validated_data) # 直接赋值就可以了
print(instance)
return instance # 将结果返回
我们来用postman演示下
(这里应该是第三条记录,取错了,但是结果是一样的!)
二、序列化器 高级用法之source
路由
url(r'^book/', views.Book.as_view()),
模型
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
def author_func(self):
return '这是' + self.author + '写的'
序列化器
class Book(serializers.Serializer):
name = serializers.CharField(max_length=32)
price = serializers.CharField()
author = serializers.CharField(min_length=1)
publish = serializers.CharField()
# 执行表模型中的 author_func方法,并把 返回值赋予 book_info
# 注意 字段名不能和 表模型方法 相同
book_info = serializers.CharField(source='author_func')
视图函数
class Book(APIView):
def get(self,request, *args, **kwargs):
books = models.Book.objects.all()
ser = serializer.Book(instance=books, many=True)
return Response(ser.data)
测试
三、模型类序列化器
原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer,跟表模型有对应关系
1.重写表结构
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
author = models.ManyToManyField(to='Author')
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
class Publish(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=64)
class Author(models.Model):
name = models.CharField(max_length=32)
salary = models.IntegerField()
phone = models.CharField(max_length=11)
2.路由配置
url(r'^bookmodel/', views.BookModel.as_view()),
3.视图配置
class BookModel(APIView):
def get(self, request, *args, **kwargs):
books = models.Book.objects.all()
ser = serializer.BookModel(instance=books, many=True)
return Response(ser.data)
4.指定字段
4.1 使用fields来明确字段,__all__
表名包含所有字段,也可以写明具体哪些字段,如
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = ['name', 'author', 'publish']
4.2 使用exclude可以明确排除掉哪些字段
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
exclude = ['author', 'publish']
4.3 默认ModelSerializer使用主键作为关联字段,但是我们可以使用depth来简单的生成嵌套表示,depth应该是整数,表明嵌套的层级数量。如:
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__' # 查询所有
depth = 1
4.4 指明只读字段
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__'
read_only_fields = ['name', 'author', 'publish']
4.5 只写字段
反序列化需要传入的字段
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__'
write_only_fields = ('name', 'author', 'publish')
4.6 添加额外参数
我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = ['name', 'author', 'publish']
depth = 1
extra_kwargs = {'name': {'min_length': 2}}
四、高级用法之SerializerMethodField
善用 SerializerMethodField 来优化不必要的查询
方案一
depth
方案二
其实在很多时候我们并不需要在查询 Book对象的时候查询所拥有的Author对象,很多时候我们只是需要一个 Book所拥有 Author对象的总数就可以了,如果有需要再去查询 Author列表详细。此时我们就可以使用 Django REST framework 提供的 SerializerMethodField 来实现这个目的
减少 depth 的使用
class BookModel(serializers.ModelSerializer):
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__'
author = serializers.SerializerMethodField()
# 名字不可以概念 实例化那个 就只能 get_字段名
def get_author(self, obj):
# name = obj.author.name # 也可以单个返回
dic = {'name':obj.author.name,'salary':obj.author.name}
return dic
方案三
使用序列化类的嵌套
class Author(serializers.ModelSerializer):
class Meta:
# 关联
model = models.Author
# 选取需要显示的字段
fields = ['name', 'salary']
class BookModel(serializers.ModelSerializer):
# 要实例化哦
# 也要和 book的字段对应
author = Author()
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__'
五、drf的请求与响应
1. Request
-data :前端以post请求提交的数据都在它中
-FILES :前端提交的文件
-query_params:就是原来的request.GET
-重写了 __getattr__
-使用新的request.method其实取得就是原生request.method(通过反射实现)
2. Response
-from rest_framework.response import Response
-data:响应的字典
-status:http响应的状态码
-drf提供给你了所有的状态码,以及它的意思
from rest_framework.status import HTTP_201_CREATED
-template_name:模板名字(一般不动),了解
-headers:响应头,字典
-content_type:响应的编码方式,了解
3. 自己封装一个response,继承drf的Response
根据restful规范,要返回状态码和错误信息,但是不同的方法中就造成了大量的代码冗余,我们就可以自己写一个response,这样我们就可以直接通过 . 来调用了
# 自己封装一个Response对象
class CommonResponse:
def __init__(self):
self.code=100
self.msg=''
@property
def get_dic(self):
return self.__dict__
4. 通过配置,选择默认模板的显示形式(浏览器方式,json方式)
drf和django一样 有一个默认的settings,这里面包含了各种显示模式,我们可以在django的setting中改变这个配置
# 通过配置,选择默认模板的显示形式(浏览器方式,json方式)
-配置文件方式(全局)
-如果没有配置,默认有浏览器和json
-drf有默认配置文件
from rest_framework.settings import DEFAULTS
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
-在视图类中配置(局部)
-粒度更小
-class BookDetail(APIView):
renderer_classes=[JSONRenderer,]
六、many=True源码分析,局部全局钩子源码解析
1 many=True
-__init__----->一路找到了BaseSerializer---》__new__决定了生成的对象是谁
2 入口是is_valid()---》BaseSerializer--》is_valid---》self._validated_data = self.run_validation(self.initial_data)
-Serializer这个类的:self.run_validation
def run_validation(self, data=empty):
value = self.to_internal_value(data) # 局部字段自己的校验和局部钩子校验
try:
self.run_validators(value)
value = self.validate(value) # 全局钩子的校验
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
七、接口幂等性,是什么,如何弄?
接口幂等性,是什么,如何弄
八、接口
什么是接口 ?
接口只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能。
个人的理解,多用于协作开发时,有不同的人在不同的类中实现接口中的各个方法。
在python中接口由抽象类和抽象方法去实现,接口是不能被实例化的,只能被别的类继承去实现相应的功能。
个人觉得接口在python中并没有那么重要,因为如果要继承接口,需要把其中的每个方法全部实现,否则会报编译错误,还不如直接定义一个class,其中的方法实现全部为pass,让子类重写这些函数。
当然如果有强制要求,必须所有的实现类都必须按照接口中的定义写的话,就必须要用接口。
其实就是统一 子类 的行为
python有三种限制子类的方式
- 抛异常限制
- abc模块限制
- 人为限制(鸭子类型)