django REST framework multi source field

核能气质少年 提交于 2020-01-02 19:20:09

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!