Closed davidcr01 closed 1 year ago
There are multiple ways to perform this. As we are not interested in every field of the user when fetching the player (is_staff, first_name, last_name... etc) we can edit the PlayerSerializer and insert into it a condition.
class PlayerSerializer(serializers.ModelSerializer):
# As a player is related to an user, it needs its seralizer
user = serializers.SerializerMethodField()
class Meta:
model = Player
fields = ('user', 'wins', 'wins_pvp', 'wins_tournament', 'xp')
def get_user(self, obj):
if self.context['request'].method == 'GET' and 'pk' not in self.context['view'].kwargs:
# If we are getting all the players, it returns only the ID and username
return {'id': obj.user.id, 'username': obj.user.username}
else:
# If we are getting only a player, it returns the UserInfoSerializer data
user_serializer = UserInfoSerializer(obj.user)
return user_serializer.data
Here, we specify that:
user
variable is now a SerializerMethodField, to specify the fields we want.get_user
method to alter the way we are consulting that variable.
This new serializer is needed to complement the modification of the PlayerSerializer. Basically, it is the same as CustomUserSerializer, but it returns fewer fields.
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id', 'username', 'last_login', 'date_joined')
Now, if we fetch the information of the player:
http://localhost/api/players/13
Result:
{
"user": {
"id": 13,
"username": "testCreate",
"last_login": "2023-05-30T10:02:12.345953Z",
"date_joined": "2023-05-30T10:02:12.345980Z"
},
"wins": 0,
"wins_pvp": 0,
"wins_tournament": 0,
"xp": 0
}
http://localhost/api/players/
Result:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"user": {
"id": 13,
"username": "testCreate"
},
"wins": 0,
"wins_pvp": 0,
"wins_tournament": 0,
"xp": 0
},
{
"user": {
"id": 19,
"username": "notstaff2"
},
"wins": 0,
"wins_pvp": 0,
"wins_tournament": 0,
"xp": 0
}
]
}
The problem has been solved by creating a new serializer UserInfoSerializer
, which returns less information than CustomUserSerializer
. This new serializer represents the needed information for the player that may be useful. Besides, this information can be again filtered in the frontend if needed.
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id', 'username', 'last_login', 'date_joined')
The serializer is the same as CustomUserSerializer
but returns the ID, username, last_login and date_joined of the user.
This new serializer is used by another new serializer, PlayerInfoSerializer
, which returns this information related to the players. This new serializer is needed to not alter the creation of the player, which needs different fields other than the fields we need when we get a player.
class PlayerInfoSerializer(serializers.ModelSerializer):
# As a player is related to an user, it needs its seralizer
user = serializers.SerializerMethodField()
class Meta:
model = Player
fields = ('id', 'user', 'wins', 'wins_pvp', 'wins_tournament', 'xp')
def get_user(self, obj):
if self.context['request'].method == 'GET' and 'pk' not in self.context['view'].kwargs:
# If we are getting all the players, it returns only the ID and username
return {'id': obj.user.id, 'username': obj.user.username}
else:
# If we are getting only a player, it returns the UserInfoSerializer data
user_serializer = UserInfoSerializer(obj.user)
return user_serializer.data
Notice that:
user
variable has changed to serializers.SerializerMethodField()
. This will be useful to choose what fields are we going to get.get_user
method is overwritten. This method says how the user
variable is got:
To select the serializer when managing the players is necessary to change the Players' viewset.
def get_serializer_class(self):
if self.action == 'create':
# Use PlayerSerializer for creating a player
return PlayerSerializer
else:
# Use PlayerInfoSerializer for other actions
return PlayerInfoSerializer
This code snippet is added to the PlayerViewSet, it specifies the serializer depending on the action is performed.
By doing this, if we get the info of a player:
GET, http://localhost/api/players/13
{
"user": {
"id": 13,
"username": "testCreate",
"last_login": "2023-05-30T10:02:12.345953Z",
"date_joined": "2023-05-30T10:02:12.345980Z"
},
"wins": 0,
"wins_pvp": 0,
"wins_tournament": 0,
"xp": 0
}
If we get all the players:
`GET, http://localhost/api/players/`
{ "user": { "id": 13, "username": "testCreate" }, "wins": 0, "wins_pvp": 0, "wins_tournament": 0, "xp": 0 }
Notice that the ID is returned. This information may be useful if we need the whole information of the user at another moment.
In this development, a new bug was found while creating a new player using the POST request over the api/players/
path. This path wasn't working properly, and the player was not being created.
To solve this, the create
method of the PlayerSerializer
has been overwritten.
class PlayerSerializer(serializers.ModelSerializer):
user = CustomUserSerializer()
class Meta:
model = Player
fields = ('user', 'wins', 'wins_pvp', 'wins_tournament', 'xp')
def create(self, validated_data):
user_data = validated_data.pop('user', None)
user = None
if user_data:
# Create the user only if user_data is provided
user = CustomUser.objects.create_user(**user_data)
player = Player.objects.create(user=user, **validated_data)
return player
This new method specifies that we need to extract the user information from the body of the request by using the pop method
, and create the user with that information if is valid. After creating the user, we create the related player.
With this improvement, we can create a player and a user simultaneously like this:
Body:
{
"user": {
"username": "john6",
"password": "test123test",
"email": "john@gmail.com"
},
"wins": 10,
"wins_pvp": 5,
"wins_tournament": 2,
"xp": 100
}
And the result is a 201 code (created) and this new user:
{
"user": {
"id": 32,
"is_superuser": false,
"username": "john6",
"first_name": "",
"last_name": "",
"email": "john@gmail.com",
"is_staff": false,
"is_active": true,
"avatar": null,
"last_login": "2023-06-01T11:29:27.852559Z",
"date_joined": "2023-06-01T11:29:27.852573Z",
"groups": [],
"user_permissions": []
},
"wins": 10,
"wins_pvp": 5,
"wins_tournament": 2,
"xp": 100
}
It is necessary to hide the users' information when the players are listed. This information has to be hidden from the players and has to be in line with their permissions, as they can not see the users' private information (first_name, last_name, email... etc)