序列化类外键字段的覆盖
'''
1.在序列化类中自定义字段,名字与model类中属性名一致,就称之为覆盖操作
- 覆盖的是属性所有规则:extra_kwargs中指定的建议规则,model字段提供的默认规则,数据库唯一约束等规则
2.外键覆盖字段用PrimaryKeyRelatedField来实现,可以做到只读、只写、可读可写三种形式
- 只读 read_only=True
- 只写 queryset = 关联表的queryset, write_only=True
- 可读可写 queryset = 关联表的queryset
3.当外键关联的数据是多个时,需要标识many=True
'''
class BookModelSerializer(serializers.ModelSerializer):
# 如何覆盖外键字段
# publish = serializers.PrimaryKeyRelatedField(read_only=True) # 只读
# publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True) # 只写
# publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) # 可读可写
publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())
authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)
class Meta:
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors')
十大接口序列化总结
'''
def __init__(self,instance=None,data=empty,**kwargs):
pass
instance:是要被赋值的对象(将要被序列化)
data:是前端请求的数据,将要数据赋值给对象 (将要被反序列化)
kwargs里接收many,partial,context
- many:操作的instance和data是多个还是单个
- partial:在修改需求时使用,可以将所有校验字段required在本次校验中设置为False(required即必须,如果为true则必须要提供。如果为false,则可以不提供,打提供了就会参与校验)
- context:用于视图类和序列化类直接传参使用(一般为一个字典,可以将希望传入的参数以key:value赋值)
'''
'''
# 单查接口
UserModelSerializer(instance=user_obj)
# 群查
UserModelSerializer(instance=queryset,many=True)
# 单增接口[assert (data,dict)]
UserModelSeriazlier(data=request.data)
# 群增接口[assert (data,list)]
UserModelSeriazlier(data=request.data,many=True)
# 单整体改
UserModelSerializer(instance=obj,data=request.data)
# 群整体改(1.Meta设置list_serializer_class,新增一个辅助类,继承ListSerializer类重写update方法 2.从request.data中分离出pks转化为queryset和包含新数据的request.data)
UserModelSerializer(instance=queryset,data=request.data,many=True)
# 单局部改
UserModelSerializer(instance=obj,data=request.data,partial=True)
# 群局部改(同整体改)
USerModelSerialzer(instance=queryset,data=request.data,many=True,partial=True)
# 删接口
不需要用序列化类
'''
案例
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)
@property # @property字段默认就是read_only,且不允许修改
def publish_name(self):
return self.publish.name
@property # 自定义序列化过程
def author_list(self):
temp_author_list = []
for author in self.authors.all():
author_dic = {
"name": author.name
}
try:
author_dic['phone'] = author.detail.phone
except:
author_dic['phone'] = ''
temp_author_list.append(author_dic)
return temp_author_list
class Publish(BaseModel):
name = models.CharField(max_length=64)
class Author(BaseModel):
name = models.CharField(max_length=64)
class AuthorDetail(BaseModel):
phone = models.CharField(max_length=11)
author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, on_delete=models.CASCADE)
ulrs.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 = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
except:
return APIResponse(1, '数据有误')
if rows: # 只要有受影响的行,就代表删除成功
return APIResponse(0, '删除成功')
return APIResponse(2, '删除失败')
# 单整体改群整体改
"""
单整体改:接口:/books/(pk)/ 数据:dict
群整体改:接口:/books/ 数据:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 单
try:
instance = models.Book.objects.get(is_delete=False, pk=pk)
except:
return APIResponse(1, 'pk error', http_status=400)
# 序列化类同时赋值instance和data,代表用data重新更新instance => 修改
serializer = serializers.BookModelSerializer(instance=instance, data=request.data)
serializer.is_valid(raise_exception=True)
obj = serializer.save()
return APIResponse(result=serializers.BookModelSerializer(obj).data)
else: # 群
""" 分析request.data数据 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...]
1)从 request.data 中分离出 pks 列表
2)pks中存放的pk在数据库中没有对应数据,或者对应的数据已经被删除了,这些不合理的pk要被剔除
3)pks最终转换得到的 objs 列表长度与 request.data 列表长度不一致,就是数据有误
"""
pks = []
try: # 只要不是要求的标准数据,一定会在下方四行代码某一行抛出异常
for dic in request.data:
pks.append(dic.pop('pk'))
objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
assert len(objs) == len(request.data) # 两个列表长度必须一致
except:
return APIResponse(1, '数据有误', http_status=400)
serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
serializer.is_valid(raise_exception=True)
objs = serializer.save()
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
# 单局部改群局部改
"""
单局部改:接口:/books/(pk)/ 数据:dict
群局部改:接口:/books/ 数据:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
def patch(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 单
try:
instance = models.Book.objects.get(is_delete=False, pk=pk)
except:
return APIResponse(1, 'pk error', http_status=400)
# partial=True就是将所有反序列化字段的 required 设置为 False(提供就校验,不提供不校验)
serializer = serializers.BookModelSerializer(instance=instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
obj = serializer.save()
return APIResponse(result=serializers.BookModelSerializer(obj).data)
else: # 群
pks = []
try: # 只要不是要求的标准数据,一定会在下方三行代码某一行抛出异常
for dic in request.data:
pks.append(dic.get('pk'))
objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
assert len(objs) == len(request.data) # 两个列表长度必须一致
except:
return APIResponse(1, '数据有误', http_status=400)
serializer = serializers.BookModelSerializer(
instance=objs,
data=request.data,
many=True,
partial=True,
context={'request': request} # 初始化时,对context赋值,将视图类中数据传递给序列化类
)
serializer.is_valid(raise_exception=True)
objs = serializer.save()
print(serializer.context) # 在完成序列化类校验后,可以重新拿到序列化类内部对context做的值更新
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
视图家族
"""
视图基类:APIView、GenericAPIView
视图工具类:mixins包下的五个类(六个方法)
工具视图类:generics包下的所有GenericAPIView的子类
视图集:viewsets包下的类
"""
""" GenericAPIView基类(基本不会单独使用,了解即可,但是是高级视图类的依赖基础)
1)GenericAPIView继承APIView,所有APIView子类写法在继承GenericAPIView时可以保持一致
2)GenericAPIView给我们提供了三个属性 queryset、serializer_class、lookup_field
3)GenericAPIView给我们提供了三个方法 get_queryset、get_serializer、get_obj
"""
""" mixins包存放了视图工具类(不能单独使用,必须配合GenericAPIView使用)
CreateModelMixin:单增工具类
create方法
ListModelMixin:群查工具类
list方法
RetrieveModelMixin:单查工具类
retrieve方法
UpdateModelMixin:单整体局部改工具类
update方法
DestroyModelMixin:单删工具类
destory方法
"""
""" generics包下的所有GenericAPIView的子类(就是继承GenericAPIView和不同mixins下的工具类的组合)
1)定义的视图类,继承generics包下已有的特点的GenericAPIView子类,可以在只初始化queryset和serializer_class两个类属性后,就获得特定的功能
2)定义的视图类,自己手动继承GenericAPIView基类,再任意组合mixins包下的一个或多个工具类,可以实现自定义的工具视图类,获得特定的功能或功能们
注:
i)在这些模式下,不能实现单查群查共存(可以加逻辑区分,也可以用视图集知识)
ii)DestroyModelMixin工具类提供的destory方法默认是从数据库中删除数据,所以一般删除数据的需求需要自定义逻辑
"""