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