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(many=True)
class Meta:
model = UseTag
fields = ("tags", "title")
def create(self, validated_data):
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
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)
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()
serializer.save()
这里是反转了关系
如果业务需求是每次添加一个子项,同时指定一个父项的话,这种方式就不适合。
例如传入的数据是:
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)
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']
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()
|