DRF框架基础五之序列化类外键字段的覆盖,十大接口和视图家族
一.序列化类外键字段的覆盖
1.先补充普通的字段覆盖
# 比如在models.py中定义了一个类名和字段名
class Book(BaseModel):
name = models.CharField(max_length=64)
# 那么在serializers.py文件中序列化的时候可以对该字段进行覆盖,比如
from rest_framwork import serializers
class BookModelSerializer(serializers.ModelSerializer):
# 在这个位置可以覆盖原字段规则,比如设置成只读
name = serializers.CharField(read_only = True)
name =
class Meta:
model = models.Book
fields = ('name')
extra_kwargs = {}
2.那怎么对外键字段进行覆盖呢
from rest_framework import serializers
from . import models
# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
# 外键字段覆盖为只读
publish = serializers.PrimaryKeyRelatedField(read_only=True)
# queryset里面存的是该外键字段关联的表的所有数据,外键字段覆盖为只写
publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects, write_only=True)
# 外键字段覆盖为可读可写
publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())
# 比如这个书可能对应多个作者,就要设置many = True
authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)
class Meta:
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors',) # 返回给前台展示的字段
extra_kwargs = {
'image': {
'read_only': True,
},
'publish': {
'write_only': True, # 如果设置write_only,则定义为只写,即反序列话,即使返回给前台字段也不显示
},
'authors': {
'write_only': True,
},
}
总结:
"""
1)在序列化类中自定义字段,名字与model类中属性名一致,就称之为覆盖操作
(覆盖的是属性的所有规则:extra_kwargs中指定的简易规则、model字段提供的默认规则、数据库唯一约束等哪些规则)
2)外键覆盖字段用PrimaryKeyRelatedField来实现,可以做到只读、只写、可读可写三种形式
只读:read_only=True
只写:queryset=关联表的queryset, write_only=True
可读可写:queryset=关联表的queryset
3)当外界关联的数据是多个时,需标识many=True条件
"""
十大接口序列化总结
"""
def __init__(self, instance=None, data=empty, **kwargs):
pass
instance:是要被赋值对象的 - 对象类型数据赋值给instance
data:是要被赋值数据的 - 请求来的数据赋值给data
kwargs:内部有三个属性:many、partial、context
many:操作的对象或数据,是单个的还是多个的
partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False
context:用于视图类和序列化类直接传参使用
"""
# 常见使用
# 单查接口
UserModelSerializer(instance=user_obj)
# 群查接口
UserModelSerializer(instance=user_query, many=True)
# 单增接口,request.data是字典
UserModelSerializer(data=request.data)
# 群增接口,request.data是列表
UserModelSerializer(data=request.data, many=True)
# 单整体改接口,request.data是字典
UserModelSerializer(instance=user_obj, data=request.data)
# 群整体改接口,request.data是列表,且可以分离出pks,转换成user_queryset
UserModelSerializer(instance=user_queryset, data=request.data, many=True)
# 单局部改接口,request.data是字典
UserModelSerializer(instance=user_obj, data=request.data, partial=True)
# 群局部改接口,request.data是列表,且可以分离出pks,转换成user_queryset
UserModelSerializer(instance=user_queryset, data=request.data, partial=True, many=True)
# 删接口,用不到序列化类
十大接口核心知识小结
"""
1)初始化序列化类,设置partial=True可以将所有反序列化字段的 required 设置为 False(提供就校验,不提供不校验),可以运用在局部修改接口
2)初始化序列化类,设置context={...},在序列化类操作self.context,实现视图类和序列化类数据互通
3)只有要完成资源的群改这种特殊需求时,才需要自定义ListSerializer绑定给自定义的ModelSerializer,重写update方法,来完成需求
"""
案例
models.py
# 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
updated_time = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True # 必须完成该配置
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
image = models.ImageField(upload_to='img', default='img/default.png')
publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
urls.py
url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
serializers.py
from rest_framework import serializers
from . import models
# 群增群改辅助类(了解)
class BookListSerializer(serializers.ListSerializer):
"""
1)create群增方法不需要重新
2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用
3)self.child就代表该ListSerializer类绑定的ModelSerializer类
BookListSerializer的self.child就是BookModelSerializer
"""
# 重新update方法
def update(self, queryset, validated_data_list):
return [
self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
]
# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
# 配置自定义群增群改序列化类
list_serializer_class = BookListSerializer
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
# fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
extra_kwargs = {
'image': {
'read_only': True,
},
'publish': { # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
'write_only': True,
},
'authors': {
'write_only': True,
},
}
# 需求:内外传参
# 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类
# 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类
# 序列化类的context属性,被视图类与序列化类共享
def validate(self, attrs):
print(self.context) # 可以获得视图类在初始化序列化对象时传入的context
# self.context.update({'a': 10}) # 序列化类内部更新context,传递给视图类
return attrs
views.py
from rest_framework.views import APIView
from . import models, serializers
from .response import APIResponse
# 六个必备接口:单查、群查、单增、单删、单整体改(了解)、单局部改
# 四个额外接口:群增、群删、群整体改、群局部改
class BookAPIView(APIView):
# 单查群查
"""
单查:接口:/books/(pk)/
群查:接口:/books/
"""
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
serializer = serializers.BookModelSerializer(instance=obj)
return APIResponse(result=serializer.data)
else:
queryset = models.Book.objects.filter(is_delete=False).all()
serializer = serializers.BookModelSerializer(instance=queryset, many=True)
return APIResponse(results=serializer.data)
# 单增群增
"""
单增:接口:/books/ 数据:dict
群增:接口:/books/ 数据:list
"""
def post(self, request, *args, **kwargs):
# 如何区别单增群增:request.data是{}还是[]
if not isinstance(request.data, list):
# 单增
serializer = serializers.BookModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
obj = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
else:
# 群增
serializer = serializers.BookModelSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
objs = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)
# 单删群删
"""
单删:接口:/books/(pk)/
群删:接口:/books/ 数据:[pk1, ..., pkn]
"""
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
pks = [pk] # 将单删伪装成群删一条
else:
pks = request.data # 群删的数据就是群删的主键们
try: # request.data可能提交的是乱七八糟的数据,所以orm操作可能会异常 rows表示受影响的行数