jam*_*mes 27 django django-rest-framework
我有一个管理多对多关系的"通过"模型,我希望能够将"直通"模型和目标模型作为平面数据返回,而不是将目标模型嵌套.
因此,使用标准示例来表示多个到一个通过,说这些是模型,
class Person(models.Model):
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
favourite_food = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Run Code Online (Sandbox Code Playgroud)
所以我目前返回会员资格的序列化工具是,
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
person = PersonSerializer()
class Meta:
model = Membership
fields = ('id', 'url', 'group', 'date_joined', 'invite_reason', 'person')
class PersonSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Person
fields = ('first_name', 'last_name', 'favourite_food')
Run Code Online (Sandbox Code Playgroud)
所以当我使用MembershipSerializer检索一个会员模型时,我得到了这个json,
{
'id':1,
'url':'http://cheeselovers.com/api/member/1/'
'group':'http://cheeselovers.com/api/group/1/'
'date_joined': '2014-01-24T16:33:40.781Z',
'invite_reason': 'loves cheese',
'person':{
'first_name':'Barry',
'last_name':'CheeseLover',
'favourite_food': 'cheese'
}
}
Run Code Online (Sandbox Code Playgroud)
但是我要归还的是这个,
{
'id':1,
'url':'http://cheeselovers.com/api/member/1/'
'group':'http://cheeselovers.com/api/group/1/'
'date_joined': '2014-01-24T16:33:40.781Z',
'invite_reason': 'loves cheese',
'first_name':'Barry',
'last_name':'CheeseLover',
'favourite_food': 'cheese'
}
Run Code Online (Sandbox Code Playgroud)
现在我意识到我可以通过将MembershipSerializer更改为此来简单地完成此操作,
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
first_name = serializers.Field(source='person.first_name')
last_name = serializers.Field(source='person.last_name')
favourite_food = serializers.Field(source='person.favourite_food')
class Meta:
model = Membership
fields = ('id', 'url', 'group', 'date_joined', 'invite_reason', 'first_name', 'last_name', 'favourite_food')
Run Code Online (Sandbox Code Playgroud)
但是,目标模型我有10个属性,中间'通'模型只有只读道具,所以我已经有一个功能的目标模型序列化器,在创建中间模型时使用.
能够重复使用它感觉更干,因此如果目标模型上的任何内容发生更改,我只需要对其序列化程序进行更改,因为这些更改将反映在中介的序列化程序返回的数据中.
那么有没有一种方法可以从PersonSerializer获取数据并将其添加到Membership数据中,以便它是平的而不是嵌套的?
...希望一切都有道理.
eku*_*ela 24
这是基于James答案的方法,但是对于更新版本的Django Rest Framework以及对读写的支持(仅更新嵌套字段,添加创建应该很容易,请参阅DRF的文档.)
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('phone', 'some', 'other', 'fields')
class UserDetailsSerializer(serializers.ModelSerializer):
"""User model with Profile. Handled as a single object, profile is flattened."""
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
read_only_fields = ('email', )
def to_representation(self, obj):
"""Move fields from profile to user representation."""
representation = super().to_representation(obj)
profile_representation = representation.pop('profile')
for key in profile_representation:
representation[key] = profile_representation[key]
return representation
def to_internal_value(self, data):
"""Move fields related to profile to their own profile dictionary."""
profile_internal = {}
for key in ProfileSerializer.Meta.fields:
if key in data:
profile_internal[key] = data.pop(key)
internal = super().to_internal_value(data)
internal['profile'] = profile_internal
return internal
def update(self, instance, validated_data):
"""Update user and profile. Assumes there is a profile for every user."""
profile_data = validated_data.pop('profile')
super().update(instance, validated_data)
profile = instance.profile
for attr, value in profile_data.items():
setattr(profile, attr, value)
profile.save()
return instance
Run Code Online (Sandbox Code Playgroud)
我不相信这是最简单的方法,但我想出的解决方案是覆盖MembershipSerializer的to_native方法,然后手动创建并调用PersonSerializer的to_native方法并将两个结果字典合并在一起
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
def to_native(self, obj):
ret = super(MembershipSerializer, self).to_native(obj)
p_serializer = PersonSerializer(obj.person, context=self.context)
p_ret = p_serializer.to_native(obj.person)
for key in p_ret:
ret[key] = p_ret[key]
return ret
class Meta:
model = Membership
fields = ('id', 'url', 'group', 'date_joined', 'invite_reason', 'person')
Run Code Online (Sandbox Code Playgroud)
字典都是SortedDict的子类.我不确定是否有一个显式方法来合并保留顺序的两个,所以我只是使用了一个循环.
詹姆斯的回答是我最终使用的.由于我有几个使用此方法的序列化程序,我将其转换为mixin:
class FlattenMixin(object):
"""Flatens the specified related objects in this representation"""
def to_representation(self, obj):
assert hasattr(self.Meta, 'flatten'), (
'Class {serializer_class} missing "Meta.flatten" attribute'.format(
serializer_class=self.__class__.__name__
)
)
# Get the current object representation
rep = super(FlattenMixin, self).to_representation(obj)
# Iterate the specified related objects with their serializer
for field, serializer_class in self.Meta.flatten:
serializer = serializer_class(context = self.context)
objrep = serializer.to_representation(getattr(obj, field))
#Include their fields, prefixed, in the current representation
for key in objrep:
rep[field + "__" + key] = objrep[key]
return rep
Run Code Online (Sandbox Code Playgroud)
这样,您可以执行以下操作:
class MembershipSerializer(FlattenMixin, serializers.HyperlinkedModelSerializer):
class Meta:
model = Membership
fields = ('id', 'url', 'group', 'date_joined', 'invite_reason')
flatten = [ ('person', PersonSerializer) ]
Run Code Online (Sandbox Code Playgroud)
我没有尝试过,HyperlinkedModelSerializer但ModelSerializer可以与您一起制作支持flatten选项的自定义序列化程序类。
class CustomModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
self.flatten = kwargs.pop('flatten', False)
super(CustomModelSerializer, self).__init__(*args, **kwargs)
def get_fields(self):
fields = super(CustomModelSerializer, self).get_fields()
for field_name, field in fields.items():
if getattr(field, 'flatten', False):
del fields[field_name]
for nested_field_name, nested_field in field.fields.iteritems():
nested_field.source = (field_name + '.' +
(nested_field.source or nested_field_name))
fields[nested_field_name] = nested_field
return fields
Run Code Online (Sandbox Code Playgroud)
用法:
class MembershipSerializer(CustomModelSerializer):
person = PersonSerializer(flatten=True)
class Meta:
model = Membership
fields = ('person', ...)
class PersonSerializer(CustomModelSerializer):
class Meta:
model = Person
fields = (...)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7524 次 |
| 最近记录: |