Как сделать POST-запрос и использовать ID автора?

У меня есть эта модель Game и у меня есть рабочий GET метод и я хотел бы создавать новые Game объекты в моей базе данных с помощью POST запроса.

У меня Game вот так:

class Game(models.Model):
    owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
    name = models.CharField(max_length=255)
    ...

У меня есть такой вид:

class GameDetail(APIView):
    def get_object(self, game_slug):
        try:
            return Game.objects.filter(slug=game_slug)
        except Game.DoesNotExist:
            raise Http404

    def get(self, request, game_slug, format=None):
        game = self.get_object(game_slug).first()
        serializer = GameSerializer(game)
        return Response(serializer.data)

    def post(self, request, format=None):
        game_data = request.data
        game_data['owner'] = { 'username': 'admin', 'email': 'admin@example.com'} // just to test

        serializer = GameSerializer(data=game_data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Если я посылаю серверу POST-запрос со следующим телом:

 {
    "name": "Posted game",
    ... // ommiting owner field
}

Похоже, что сервер думает, что я хочу создать нового пользователя, но я просто хочу, чтобы игра была связана с userId

{
    "owner": {
        "username": [
            "user with this username already exists."
        ]
    }
}

Если вместо этого я устанавливаю game_data['owner'] непосредственно в идентификатор пользователя, он жалуется, что ожидает словарь.

Как я могу сделать так, чтобы когда я GET играю, я видел владельца (имя пользователя и email) и когда я пишу сообщение, оно может просто добавить вошедшего пользователя?

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("username", "email")

class GameSerializer(serializers.ModelSerializer):
    owner = UserSerializer()
    class Meta:
        model = Game
        fields = (
            "name",
            "owner",
            ...
        )

Иногда лучшим решением является создание двух сериализаторов и использование нужного для нужной конечной точки:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("username", "email")

class ReadGameSerializer(serializers.ModelSerializer):
    owner = UserSerializer()

    class Meta:
        model = Game
        fields = (
            "name",
            "owner",
            ...
        )

class WriteGameSerializer(serializers.ModelSerializer):

    class Meta:
        model = Game
        fields = (
            "name",
            "owner",
            ...
        )

Затем обновите представление:

class GameDetail(APIView):
    def get_object(self, game_slug):
        try:
            return Game.objects.filter(slug=game_slug)
        except Game.DoesNotExist:
            raise Http404

    def get(self, request, game_slug, format=None):
        game = self.get_object(game_slug).first()
        serializer = ReadGameSerializer(game)
        return Response(serializer.data)

    def post(self, request, format=None):
        game_data = request.data
        game_data['owner'] = request.user # if the user is authenticated

        serializer = WriteGameSerializer(data=game_data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Другим решением было бы сделать вложенный сериализатор записываемым, но по опыту это решение сложнее поддерживать.

Существует множество способов ассоциирования, Если вы хотите ассоциировать вошедшего пользователя с игрой, то передайте запрос в сериализаторе

...
    def post(self, request, format=None):
        serializer = GameSerializer(data=request.data, context={"request": requeset})
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Модифицируйте ваш сериализатор, serializers.CharField(default=serializers.CurrentUserDefault()) это автоматически получит вашего пользователя из запроса и свяжет его с полем owner в Game Model

Переопределите метод to_representation для манипулирования данными ответа

class GameSerializer(serializers.ModelSerializer):
    owner = serializers.CharField(default=serializers.CurrentUserDefault())
    class Meta:
        model = Game
        fields = (
            "name",
            "owner",
            ...
        )

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['owner'] = {"username":instance.owner.username, "email": instance.owner.email}
        return representation
Вернуться на верх