多表序列化与反序列化

文章详细介绍了如何在DjangoRestFramework(DRF)中使用ModelSerializer进行数据模型的序列化,并探讨了如何通过source、SerializerMethodField和read_only/write_only字段定制返回格式,以及处理多表关联和只读/只写字段的情况。
摘要由CSDN通过智能技术生成

前提准备

首先准备几张表,以图书为例,创建图书表,作者表,出版社表和第三张关系表。然后创建一个serializer.py文件,目的就是写类,继承serializer。分发路由。

创建表

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    publish_date = models.DateTimeField()

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

创建serializer.py文件

略。。。

定制返回格式之source

source是一个序列化器的字段选项,用于指定序列化器中字段的源。

总结:

  1. source定制返回字段名,跟字段必须对应
  2. 可以跨表查询

ps小知识:所有字段都可以被转成CharField格式。

当我们想要在后期实现下面格式的数据:

{name:书名,price:价格,publish:{name:出版社名,addr:地址},authors:[{},{}]}

那我们该如何做呢?

定制返回字段

方法一:在表模型中写方法,然后在序列化类中做映射

models.py:

serializer.py

效果:

SerializerMethodField字段

方法二:通过SerializerMethodField方法实现:

只需要将后面的字段参数改为SerialzierMethodField字段,然后定义函数返回就可以了。

models.py:

serializer.py

子序列化

方法三:子序列化

反序列化保存

介绍两个字段:

  • read_only (仅序列化)
  • write_only (仅反序列化)

总结:

  • 反序列化字段可以随意命名,与表字段无关,但是后续保存和修改要对应好。
  • 需要重写updata和create,保存逻辑需要自己写。

view.py

from rest_framework.views import APIView
# Create your views here.
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response

class BookView(APIView):
    # 查询所有
    def get(self, request):
        obj_list = Book.objects.all()
        serializer = BookSerializer(instance=obj_list, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response('添加成功')
        else:
            return Response({'code':100, 'msg':serializer.errors})

class BookDetailView(APIView):
    # 查询单条
    def get(self, request, pk):
        obj = Book.objects.all().filter(pk=pk).first()
        serializer = BookSerializer(instance=obj)
        return Response(serializer.data)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).first().delete()
        return Response('删除成功')


    def put(self, request, pk):
        # obj = Book.objects.all().filter(pk=pk).first()
        serializer = BookSerializer(instance=pk,data=request.data)
        if serializer.is_valid():
            return Response('修改成功')
        else:
            return Response({'code': 100, 'msg': serializer.errors})

serializer.py

from rest_framework import serializers
from .models import Book
# class BookSerializer(serializers.Serializer):
#     # source 定制返回字段名,跟name 必须对应
#     name = serializers.CharField(source='name')
#     # 显示出版社名字是字符串类型-->可以跨表查询
#     publish_name = serializers.CharField(source='publish.name')
#     publish_id = serializers.IntegerField(source='publish.id')
#     price = serializers.DecimalField(max_digits=5,decimal_places=2)
#     publish_date = serializers.DateTimeField()


# class BookSerializer(serializers.Serializer):
#     book_name = serializers.SerializerMethodField()
#     def get_book_name(self, obj):
#         return obj.name
#     price = serializers.DecimalField(max_digits=5, decimal_places=2)
#     publish_date = serializers.DateTimeField()
#     # 出版社对象
#     publish_detail = serializers.SerializerMethodField()
#     # 配合一个方法,必须要get_字段名
#     def get_publish_detail(self, obj):
#         return {'name': obj.publish.name, 'city': obj.publish.city, 'id': obj.pk}
#     # 所有作者
#     authors_list = serializers.SerializerMethodField()
#     def get_authors_list(self, obj):
#         l = []
#         for author in obj.authors.all():
#             l.append({'name': author.name, 'age': author.age, 'id': author.id})
#         return l


class PublishSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    city = serializers.CharField()
    email = serializers.EmailField()

class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.CharField()

from datetime import datetime
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish_date = serializers.DateTimeField(required=False, default=datetime.now)
    # 这个字段仅序列化
    publish_detail = PublishSerializer(source='publish',read_only=True)
    # 这个字段仅序列化
    authors_list = AuthorSerializer(source='authors', many=True, read_only=True)

    # 反序列化(如果是多表关联)
    publish = serializers.IntegerField(write_only=True) # 前端传入数字,自动跟出版社对应

    authors = serializers.ListField(write_only=True)

    def create(self, validated_data):
        publish_id = validated_data.pop('publish')
        authors = validated_data.pop('authors')
        book = Book.objects.create(**validated_data, publish_id=publish_id)
        book.authors.add(*authors)
        return book

    def update(self, instance, validated_data):
        publish_id = validated_data.pop('publish')
        authors = validated_data.pop('authors')
        book_qs = Book.objects.filter(pk=instance)# 查询qs对象
        book_qs.update(**validated_data, publish_id=publish_id) # 使用qs更新
        instance = book_qs.first()  # 查单个对象
        instance.authors.set(authors)  # 向中间表中插入数据
        return instance

ModelSerializer使用

在之前写序列化类,没有显示指明跟哪个表一一对应,ModelSerializer可以和表做一一对应关系。

如图,这样可以省去很多代码,但是如果想要额外加一些字段,如下操作(仅序列化除外):

仅序列化需要在与class Meta同级下额外添加字段,而class Meta相当于drf自动帮你写好了那些基本字段。

总结:

  1. ModelSerializer可以和表做一一对应关系
  2. 局部钩子全局钩子和之前一样写(注意层级)
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值