IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Django REST FrameWork 序列化关联一对多 多对多关系 -> 正文阅读

[Python知识库]Django REST FrameWork 序列化关联一对多 多对多关系

Django REST FrameWork 序列化关联一对多 多对多关系

如果对一个含有多对多、外键的模型进行序列化,这时候这些关联的字段会只展示id

外键序列化(ForeignKey)&多对多序列化(manytomany)

ManyToMany 多对多序列化

model.py

class TestTag(models.Model):
    name = models.CharField("标签名", max_length=100)

    class Meta:
        verbose_name = "标签"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class UseTag(models.Model):
    title = models.CharField("标题", max_length=70)
    tags = models.ManyToManyField(TestTag, verbose_name="标签", blank=True)

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

方法一 只读

Serializer.py

class TestTagSerializer(serializers.ModelSerializer):
    class Meta:
        model = TestTag
        fields = "__all__"

    def to_representation(self, value):
        return value.name

from TestModel.models import TestTag

class TestSerializer(serializers.ModelSerializer):
    tags = TestTagSerializer(read_only=True,many=True)

    class Meta:
        model = UseTag
        fields = "__all__"

views.py

class TestTagList(generics.ListCreateAPIView):
    queryset = UseTag.objects.all()
    serializer_class = TestSerializer

测试: python manage.py shell

from TestModel.serializers import TestSerializer
data = {"title":"test","tags":[{"name":"tag1"},{"name":"tag2"}]}
s = TestSerializer(data=data)
s.is_valid()
s.save()

这个可以取 和 存,取可以完整的取,但是存不能存tag,因为read_only=True

拓展:tag自定义字段

方法1:使用depth

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("text", "tag")
        depth = 1 

方法2:重写TagSerializer的to_representation方法

class PostSerializer(serializers.ModelSerializer):
    tag = TagSerializer(read_only=True, many=True)

    class Meta:
        ...

class TagSerializer(serializers.RelatedField):

     def to_representation(self, value):
         return value.name

     class Meta:
        model = Tag

方法二 可读可取

在方法一的基础上修改TestSerializer

class TestSerializer(serializers.ModelSerializer):
    tags = TestTagSerializer(read_only=True,many=True)
    tags_id = serializers.PrimaryKeyRelatedField(
    queryset=TestTag.objects.all(), write_only=True, many=True)

    class Meta:
        model = UseTag
        fields = ("tags", "title", 'tags_id')

    def create(self, validated_data):
        tags_id_data = validated_data.pop('tags_id')
        usetag = UseTag.objects.create(**validated_data)
        for tag_data in tags_id_data:
            usetag.tags.add(tag_data)

        return usetag

这里添加了一个tags_id 属性是write_only=True只有写的时候生效,并且可以有多个(many=True),指定queryset为TestTag的model。

重写了create方法,在创建的时候传入:

{
    "title": "tttt",
    "tags_id": [1,2,3,4,5]
}

tags_id即为tag对应的id,创建时先将tags_id弹出,然后创建usetag对象,再根据id(主键)来给usetag对象添加tag。

这个方法的缺点是不能创建新的tag,只能指定已有的tag。

改进:

class TestSerializer(serializers.ModelSerializer):
    # tags = TestTagSerializer(read_only=True,many=True)
    tags = TestTagSerializer(many=True)
    # tags_id = serializers.PrimaryKeyRelatedField(
    #     queryset=TestTag.objects.all(), write_only=True, many=True)

    class Meta:
        model = UseTag
        fields = ("tags", "title")  # , 'tags_id'

    def create(self, validated_data):
        # tags_id_data = validated_data.pop('tags_id')
        tags_data = validated_data.pop('tags')
        usetag = UseTag.objects.create(**validated_data)

        for tag_data in tags_data:
            tag_instance, created = TestTag.objects.get_or_create(name=tag_data["name"])
            usetag.tags.add(tag_instance)
        return usetag

此时不需要传入tags_id,删除tags_id,并且将tags的read_only删掉。

传入{“title”:“test”,“tags”:[{“name”:“tag1”},{“name”:“tag2”}]}

如果有tag就获取,没有就创建。如果tag有重名的会报错。

这里的tags没有用到TestTagSerializer来反序列化,只起到了输入校验的作用。

如果要使用tagid可以用如下方式:

class PostSerializer(serializers.ModelSerializer):
    tags = TagsReadOnly(many=True)

    class Meta:
        model = PostModel
        fields = '__all__'

    def to_internal_value(self, data):
        return data

    def create(self, validated_data):
        tags_data = validated_data.pop('tags')
        post = PostModel.objects.create(**validated_data)
        tags = [TagModel.objects.get(
            pk=id) for id in tags_data]
        post.tags.set(tags)
        post.save()
        return post
{
    "tags": [1, 2, 3],
    "title": "An interesting post"
}

ForeignKey 外键序列化

官方的例子:

model.py

class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)


class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ['album', 'order']
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)

serializers.py

# 注意这里创建的Serializer的关系与model相反
# 在model里 Track外键连Album
# 在Serializer里 AlbumSerializer连TrackSerializer TrackSerializer没有Album的Field
# 而在View里使用的是AlbumSerializer

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']


class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']
        
    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        # 这里Album是唯一的 而Track是多个的
        # 所以创建的时候先pop删除了track 然后创建唯一的Album 再创建Track 绑定同一个Album
        for track_data in tracks_data:
        	Track.objects.create(album=album, **track_data)
        return album

测试

from TestModel.serializers import AlbumSerializer,TestTrackSerializer
data = {
    'album_name': 'Album1',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
    ],
}
serializer = AlbumSerializer(data=data)
serializer.is_valid()#True
serializer.save()#<Album: Album object>

这里是反转了关系

如果业务需求是每次添加一个子项,同时指定一个父项的话,这种方式就不适合。

例如传入的数据是:

from TestModel.serializers import TestTrackSerializer
data = {
    'album_id': 1,
    'order': 1,
    'title': 'Public Service Announcement', 
    'duration': 245
}
t = TestTrackSerializer(data=data)
t.is_valid()
t.save()

此时仍可以使用原来的代码反序列化,但是面向的对象与实际的不一样(应该是创建Track而不是Album)

很简单:

class TestTrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration','album_id']

这里Track的model里生成的album外键是album_id,所以传入的是album_id,但是这里不能保存。

从view层创建

参照:django rest framework 向数据库中插入数据时处理外键的方法

https://www.cnblogs.com/lowmanisbusy/p/9125454.html

外键只有一个值的情况

class a(models.Model):
    name = models.CharField("分类名", max_length=100)

    class Meta:
        verbose_name = "分类"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class b(models.Model):
    title = models.CharField("标题", max_length=70)
    category = models.ForeignKey(
        a,
        verbose_name="分类",

        on_delete=models.CASCADE)

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name
class Testa(serializers.ModelSerializer):
    class Meta:
        model = a
        fields = ['name']

class Testb(serializers.ModelSerializer):
    category = Testa(read_only=False)
    class Meta:
        model = b
        fields = ['title', 'category']

    def create(self, validated_data):
        a_data = validated_data.pop('category')
        a_obj, res = a.objects.get_or_create(name = a_data['name'])
        validated_data['category'] = a_obj

        b_instance= b.objects.create(**validated_data)

        return  b_instance
from TestModel.serializers import Testb
data = {
    'title': 'Public Service Announcement5', 
    'category': {"name":"test4"}
}
t = Testb(data=data)
t.is_valid()
t.save()

注意category里是字典

外键里有多个值 同时创建外键

class category_f(models.Model):
    name = models.CharField("分类名", max_length=100)
    info = models.CharField("介绍", max_length=100)#新增info

    class Meta:
        verbose_name = "分类"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class post_f(models.Model):
    title = models.CharField("标题", max_length=70)
    category = models.ForeignKey(
        category_f,
        verbose_name="分类",

        on_delete=models.CASCADE)

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title
class test_category_f(serializers.ModelSerializer):
    class Meta:
        model = category_f
        fields = ['name','info'] #新增info

class test_post_f(serializers.ModelSerializer):
    category = test_category_f(read_only=False)
    class Meta:
        model = post_f
        fields = ['title', 'category']

    def create(self, validated_data):
        category_data = validated_data.pop('category')
        category_instance, res = category_f.objects.get_or_create(name=category_data['name'],info=category_data['info'])
        validated_data['category'] = category_instance

        post_f_instance= post_f.objects.create(**validated_data)

        return  post_f_instance

创建的时候新增一个键即可

def create(self, validated_data):
    category_data = validated_data.pop('category')
    category_instance, res = category_f.objects.get_or_create(name='photo', info="test")
    validated_data['category'] = category_instance

    post_f_instance= post_f.objects.create(**validated_data)

    return  post_f_instance

测试:

from TestModel.serializers import test_post_f
data = {
    'title': 'Public Service Announcement5', 
    'category': {"name":"test54","info":"test info"} 
}
t = test_post_f(data=data)
t.is_valid()
t.save()

同时使用外键和多对多

model

class TestTag(models.Model):
    name = models.CharField("标签名", max_length=100)

    class Meta:
        verbose_name = "标签"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name
        

class category_f(models.Model):
    name = models.CharField("分类名", max_length=100)
    info = models.CharField("介绍", max_length=100)

    class Meta:
        verbose_name = "分类"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class post_fm(models.Model):
    title = models.CharField("标题", max_length=70)
    category = models.ForeignKey(
        category_f,
        verbose_name="分类",

        on_delete=models.CASCADE)
    tags = models.ManyToManyField(TestTag, verbose_name="标签", blank=True)

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title
class test_category_f(serializers.ModelSerializer):
    class Meta:
        model = category_f
        fields = ['name','info']
        
class TestTagSerializer(serializers.ModelSerializer):
    class Meta:
        model = TestTag
        fields = "__all__"

    def to_representation(self, value):
        return value.name
        
class test_post_fm(serializers.ModelSerializer):
    category = test_category_f(read_only=False)
    tags = TestTagSerializer(many=True)

    class Meta:
        model = post_fm
        fields = ['title', 'category','tags']

    def create(self, validated_data):
        category_data = validated_data.pop('category')
        category_instance, res = category_f.objects.get_or_create(name=category_data['name'],info=category_data['info'])
        validated_data['category'] = category_instance

        tags_data = validated_data.pop('tags')

        post_fm_instance= post_fm.objects.create(**validated_data)

        for tag_data in tags_data:
            tag_instance, created = TestTag.objects.get_or_create(name=tag_data["name"])
            post_fm_instance.tags.add(tag_instance)

        return  post_fm_instance

这里就是将两个的create结合起来就行了,测试:

from TestModel.serializers import test_post_fm
data = {
    'title': 'Public Service Announcement5', 
    'category': {"name":"test54","info":"test info"},
    "tags":[{"name":"tag1"},{"name":"tag2"}]
}
t = test_post_fm(data=data)
t.is_valid()
t.save()
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-02 10:46:25  更:2021-08-02 10:47:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:37:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码