Используйте to_representation для объединения полей из связанных моделей в Django Rest Framework

У меня есть следующие модели:-

class Player(models.Model):
    first_name = models.CharField()
    etc.

class Game(models.Model):
    date = models.DateField()
    etc.

class GameMembership(models.Model):
    player = models.ForeignKey(Player, related_name="memberships")
    game = models.ForeignKey(Game, related_name="memberships")
    available = models.BooleanField(default=False)

Я создал ModelViewSet для возврата всех игроков, но я хотел бы иметь возможность для каждого игрока в списке возвращать список их игровых членств. Я могу сделать это, довольно легко, но данные, которые он возвращает, выглядят следующим образом:-

{
    "id": "1",
    "memberships": [
        {
            "available": True,
            "game": {
                "date": "a date",
                etc.
            }
        },
        {
            "available": False,
            "game": {
                "date": "a date",
                etc.
            }
        }
    ]
}

но я хотел бы скрыть аспект "членства" моей базы данных от пользователей API и вместо этого вернуть что-то вроде этого:-

{
    "id": "1",
    "games": [
        {
            "available": True,
            "date": "a date",
            etc.
        },
        {
            "available": False,
            "date": "a date",
            etc.
        }
    ]
},

Итак, я хочу взять поле (или два) из модели GameMembership и объединить его со всеми полями из модели Game, но, что очень важно, я хочу, чтобы все это было в одном словаре в возвращаемых результатах. Я знаю, что могу просто сериализовать Game на GameMembershipSerializer, но это означает, что я буду возвращать:-

{
    "id": "1",
    "games": [
        {
            "available": True,
            "game": {
                "date": "a date",
                etc.
            }
        },
        {
            "available": False,
            "game": {
                "date": "a date",
                etc.
            }
        }
    ]
}

что на самом деле не имеет смысла, поскольку пользователю придется обращаться к таким вещам, как results['games'][1]['game'], что кажется неправильным.

Я думал, что смогу сделать это, используя to_representation в GameMembershipSerializer, но я не могу понять этого.

Есть идеи?

Другой подход, не переопределяя to_representation, заключается в использовании SerializerMethodField и выполнении обработки следующим образом:

class GameModelSerializer(models.ModelSerializer):
    class Meta:
        model = Game
        fields = '__all__'


class PlayerModelSerializer(models.ModelSerializer):
    games = serializers.SerializerMethodField()

    class Meta:
        model = Player
        fields = ('id', 'games')

    def get_games(self, player)
        return [{
            'available': membership.available,
            **GameModelSerializer(membership.game).data,
        } for membership in player.memberships.all()]

Для того, чтобы убедиться, что сериализатор не обращается к db за членством только для получения игры, вы можете предварительно получить членство с выбранной игрой следующим образом:

Player.objects.prefetch_related(
    Prefetch('memberships', queryset=GameMembership.objects.select_related('game'))
)

Для тех, кому интересно, как я решил эту проблему, мне нужно было использовать to_representation, так как это должно было быть расширяемое поле (используя drf-flex-fields). Я также обнаружил, что следующая реализация была намного быстрее, чем код, упомянутый выше, поэтому я использовал ее вместо этого:-

# PlayerSerializer

games = GameMembershipGameSerializer(many=True)


class GameMembershipGameSerializer(serializers.ModelSerializer):
    class Meta:
        model = GameMembership
        fields = ["available", "game"]

    def to_representation(self, membership):
        representation = super().to_representation(membership)
        game = representation.pop("game")

        for key in game:
            representation[key] = game[key]

        return representation
Вернуться на верх