Home > front end >  How to make a POST request and use the ID of the author?
How to make a POST request and use the ID of the author?

Time:07-26

I have this model Game and I have a working GET method and would like to create new Game objects in my database by using a POST request.

I have Game like this:

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

I have a view like this:

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': '[email protected]'} // 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)

If I send the server a POST request with the following body:

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

It looks like the server thinks I want to create a new user, but I just want the game to be associated with the userId

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

If instead I set the game_data['owner'] to the userID directly it complains that it's expecting a dictionary.

How can I make it so that when I GET a game I see the owner (username and email) and when I post it can just add the logged in user?

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

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

CodePudding user response:

Sometimes the best solution is to create 2 serializers and use the right one for the right endpoint:

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",
            ...
        )

And then update your view:

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)

Another solution would be to make the nested serializer writable, but out of experience this solution is more difficult to maintain.

CodePudding user response:

There area many ways to associate, If you want to associate the logged in user with Game, than pass request in serializer

...
    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)

Modify your serializer, serializers.CharField(default=serializers.CurrentUserDefault()) this will automatically get your user from request and will associate with owner field in Game Model

Override the to_representation method to manipulate the response data

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
  • Related