drf序列化与反序列化,基表的概念
在序列化与反序列化之前,需要了解的知识点
模型表知识点
基表的概念:
创建的基表其他表可以继承,能继承其中的字段,用于许多张表共有几个字段时,可以使用基表
基表创建方式:
class BaseModel(models.Model): create_time = models.DateTimeField(auto_now_add=True) is_delete = models.BooleanField(default=False) class Meta: # abstract默认为False # abstract为True不会创建此表 abstract = True
外键字段中的断连接,反向查询与on_delete
class AuthorDetail(BaseModel): adders = models.CharField(max_length=255) mobile = models.CharField(max_length=11) author = models.OneToOneField( to='Author', # 反向查询都是通过related_name,默认情况都是表名小写 # 用原生orm时,反向查询结果为多个时,需要加_set,但是一旦定义related_name,反向查询结果为多个时,也不需要加_set related_name='detail', # 断关联 # 减缓数据库压力 db_constraint=False, # on_delete 默认情况下是models.CASCADE # DO_NOTHING author记录删除,详情表不做任何修改 # SET_DEFAULT 作者记录被删除时,author一对一字段会变成你设置的默认值,不过你要提前设置default # SET_NULL 作者记录被删除时,author一对一字段会变成空(null) 也要提前设置null=True on_delete=models.DO_NOTHING ) class Meta: db_table = 'day71_author_detail' verbose_name = '作者详情' verbose_name_plural = verbose_name def __str__(self): return '%s的详情' % self.author.name
注:
断连情况下,两张表已经没有数据库关系了,再去操作数据库不会级联更新级联删除
我们加的on_delete=models.CASCADE是让orm通过逻辑进行级联删除
序列化与反序列化(ModelSerializer)
序列化
class Publishserializers(serializers.ModelSerializer): class Meta: model = models.Publish fields = ('name', 'adders') class Bookserializers(serializers.ModelSerializer): # 自定义字段必须写在fields中 # 了解: 该方式设置的序列化字段,必须在fields中声明 # 自定义连表深度 - 子序列化方式 # 如果自定义字段与model类中的字段重名时,会走自定义字段 publish = Publishserializers() class Meta: model = models.Book fields = ('name', 'price', 'publish', 'get_author', 'gender') # 所有字段 # fields = '__all__' # 与fields不共存,exclude排除哪些字段 # exclude = ('id', 'is_delete', 'create_time') # 自动连表深度 # depth = 1
序列化fields中可以添加模型类中序列化插拔属性,
说的简单点就是,在模型类中定义方法,最好是用property装饰器装饰
# 序列化插拔式属性 - 完成自定义字段名完成连表查询 @property def get_author(self): # print(self.authors, type(self.authors)) return self.authors.values('name', 'age')
模型类中的插拔式属性,在序列化中的fields可写可不写
注:
自定义字段最好不要与模型表中的字段重名
反序列化
class BookDeserializers(serializers.ModelSerializer): class Meta: model = models.Book fields = ('name', 'price', 'publish', 'authors') # extra_kwargs为了反序列化时添加系统校验 extra_kwargs = { 'name': { 'max_length': 10, 'min_length': 3, # required默认为True,所以默认为必填字段 'required': True, 'error_messages': { 'max_length': '姓名最长10位', 'min_length': '姓名最短3位', 'required': '姓名必填' } }, } # 局部钩子 def validate_name(self, value): if 'g' in value: raise exceptions.ValidationError('你这个鸡贼') return value # 全局钩子 def validate(self, attrs): # 外键字段前端传过来的都是id值,但是后端会将其转成对象, # 如果传过来的id值,并不在数据库中,会直接报错,如果我们写了raise_exception=True会直接将错返回给前端 book_obj = models.Book.objects.filter(publish=attrs.get('publish'), name=attrs.get('name')) # print(book_obj) if book_obj: raise exceptions.ValidationError({"name": '书名重复'}) return attrs
序列化与反序列化可以合并,上面只是方便演示
""" 1) fields中设置所有序列化与反序列化字段 2) extra_kwargs划分只序列化或只反序列化字段 write_only:只反序列化 read_only:只序列化 自定义字段默认只序列化(read_only) 3) 设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则 """ class V2Bookserializers(serializers.ModelSerializer): class Meta: model = models.Book fields = ('name', 'img', 'price', 'authors', 'publish', 'publish_name', 'get_author') extra_kwargs = { 'img': { 'read_only': True }, 'publish_name': { 'read_only': True }, 'get_author': { 'read_only': True }, 'name': { 'max_length': 10, 'min_length': 3, 'error_messages': { 'max_length': '姓名最长10位', 'min_length': '姓名最短3位', 'required': '姓名必填' } } } def validate_name(self, value): if 'g' in value: raise exceptions.ValidationError('你这个鸡贼') return value def validate(self, attrs): book_obj = models.Book.objects.filter(name=attrs.get('name'), publish=attrs.get('publish')) if book_obj: raise exceptions.ValidationError({'name': '同一出版社书名不能重复'}) return attrs
再次提醒:
自定义连表深度(自定义属性)默认为read_only只参加序列化
##### 视图层:views.py ```python class V2Book(APIView): # 单局部改:对 v2/books/(pk)/ 传的数据,数据字段key都是选填 # 群局部改:对 v2/books/ # 请求数据 - [{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}] def patch(self, request, *args, **kwargs): request_data = request.data pk = kwargs.get('pk') # 将单改,群改的数据都格式化成 pks=[要需要的对象主键标识] | request_data=[每个要修改的对象对应的修改数据] if pk and isinstance(request_data, dict): # 单改 pks = [pk, ] request_data = [request_data, ] elif not pk and isinstance(request_data, list): # 群改 pks = [] for dic in request_data: # 遍历前台数据[{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}],拿一个个字典 pk = dic.pop('pk', None) if pk: pks.append(pk) else: return Response({ 'status': 1, 'msg': '数据有误', }) else: return Response({ 'status': 1, 'msg': '数据有误', }) # pks与request_data数据筛选, # 1)将pks中的没有对应数据的pk与数据已删除的pk移除,request_data对应索引位上的数据也移除 # 2)将合理的pks转换为 objs objs = [] new_request_data = [] for index, pk in enumerate(pks): try: # pk对应的数据合理,将合理的对象存储 obj = models.Book.objects.get(pk=pk) objs.append(obj) # 对应索引的数据就需要保存下来 new_request_data.append(request_data[index]) except: # 重点:反面教程 - pk对应的数据有误,将对应索引的data中request_data中移除 # index = pks.index(pk) # request_data.pop(index) continue book_ser = serializers.V2BookModelSerializer(instance=objs, data=new_request_data, partial=True, many=True) book_ser.is_valid(raise_exception=True) book_objs = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.V2BookModelSerializer(book_objs, many=True).data }) ```