问题
Let's say that I have these in my models.py :
#models.py
class Theme(models.Model):
"""An theme is an asset of multiple levels."""
adventure = models.ForeignKey(Adventure)
offset = models.PositiveSmallIntegerField()
finished = models.BooleanField(default=False)
class Level(models.Model):
"""Abstract level representation"""
theme = models.ForeignKey(Theme)
offset = models.PositiveSmallIntegerField()
finished = models.BooleanField(default=False)
class Meta:
abstract = True
class PuzzleLevel(Level):
"""A level for the Puzzle game"""
points = models.CharField(max_length=200)
image = models.ImageField()
class ImageAndWordLevel(Level):
"""A level for the ImageAndWord game"""
word = models.CharField(max_length=30)
image = models.ImageField()
And I want to use Theme in my api.
To do so, I need to serialize it:
#serializers.py
class PuzzleLevelSerializer(serializers.ModelSerializer):
image = serializers.Field(source="image.url")
class Meta:
model = PuzzleLevel
fields = ("offset", "finished", "points", "image")
class ImageAndWordLevelSerializer(serializers.ModelSerializer):
image = serializers.Field(source="image.url")
class Meta:
model = ImageAndWordLevel
fields = ("offset", "finished", "word", "image")
class ThemeSerializer(serializers.ModelSerializer):
levels = serializers.Field(source="level_set")
class Meta:
model = Theme
fields = ("offset", "finished", "levels")
Unfortunaly, I can't use the level_set source as it doesn't exist.
How could I combine puzzlelevel_set and imageandwordlevel_set in a single levels field ?
回答1:
Actually, after some digging into REST framework's source code, I came into a solution :
I wrote a custom field able to manage multiple sources.
class MultiSourceField(serializers.Field):
"""
A custom field to use when you want to
join multiple sources into a single field.
Example :
my_field = MultiSourceField(source=["attr1.subattr", "attr2.subattr"])
It can also handle serializers, a small example :
rel_field = MultiSourceField(source=[Serializer(source="rel"), "attr2"])
"""
def field_to_native(self, obj, field_name):
if obj is None:
return self.empty
sources = self.source
value = []
for source in sources:
if isinstance(source, serializers.BaseSerializer):
value += source.field_to_native(obj, "")
else:
#setting self.source to source in order to use the parent method
self.source = source
value.append(super(MultiSourceField, self).field_to_native(obj,
field_name))
#reverting self.source after the parent method call
self.source = sources
return value
Now, my theme serializer looks like this :
class ThemeSerializer(serializers.ModelSerializer):
levels = MultiSourceField(source=[
PuzzleLevelSerializer(source="puzzlelevel_set"),
ImageAndWordLevelSerializer(source="imageandwordlevel_set")
])
class Meta:
model = Theme
fields = ("offset", "finished", "levels",)
回答2:
I have done this as
student = StudentSerializer(source="from_person") or
StudentSerializer(source="to_person")
Thanks
回答3:
You can use MethodFiel (DRF=3.x)
Not Tested it
serializers.SerializerMethodField()
levels = serializers.SerializerMethodField()
def get_levels(self, obj):
value = []
s1 = PuzzleLevelSerializer(obj, many=True, source="puzzlelevel_set")
s2 = ImageAndWordLevelSerializer(obj, many=True, source="imageandwordlevel_set")
value.extend(s1.data)
value.extend(s2.data)
return value
来源:https://stackoverflow.com/questions/26782993/django-rest-framework-multi-source-field