0

Context:

I have written a simple django rest framework app as BE and a react JS app as FE. The db is SQLite and it's gonna stay that way, since there are only 2 simple models and the number of users will be quite limited as well. For the sake of the example let's assume there is only one team currently with name="First" and id=1.

Requirements:

  1. display in a table the list of players with team as a name, not its id.
  2. add players from form

Code:

models.py

class Player(models.Model):
    first_name = models.Charfield(max_length=50)
    last_name = models.Charfield(max_length=50)
    team = models.ForeignKey(Team, on_delete=models.SET_NULL, blank=True, null=True)

class Team(models.Model):
    name = models.Charfield(max_length=50)

    def __str__(self):
        return f"{self.name}"

views.py

class PlayersView(APIView):
    def post(self, request):
        serializer = PlayerSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

In order to meed 1st requirement I've implemented serializer like this:

serializers.py

class PlayerSerializer(serializers.ModelSerializer):
    team = serializers.CharField(source="team.name", read_only=True)
    class Meta:
        model = Player
        fields = "__all__"

That worked fine, but I wasn't able to add the players to the database when processing the request from the FE.

The body of that POST request is:

body

{
"first_name": "John",
"last_name": "Doe",
"team": 1
}

Looking on SO I found:

I tried sending the request body as (the FE has full info on team, both name and id, that part is working fine):

body

{
"first_name": "John",
"last_name": "Doe",
"team": "First"
}

But that was a dead end.

I played with serializer and tried: serializers.py

class PlayerSerializer(serializers.ModelSerializer):
    team = serializers.PrimaryKeyRelatedField(source="team.name", queryset=Team.objects.all())
    
    def create(self, validated_data):
        validated_data["team"] = validated_data["team"]["name"] # Since the Player initializer expected team to be an instance of Team and not a str/name neither an int/id
        return Player(**validated_data)

    def to_representation(self, instance):
        r ={
         "id": instance.id,
         "first_name": instance.first_name,
         "last_name": instance.last_namem
         "team": instance.team.name if instance.team else ""
         }
        return r
    
    class Meta:
        model = Player
        fields = "__all__"

The above does not raise any exceptions, but it doesn't save the new entry into db neither. When I logged the serializer.data after validation the id of the new item was None. I'd appreciate if somebody could either tell me where the mistake is and/or point me towards a good solution of the problem.

Gameplay
  • 1,142
  • 1
  • 4
  • 16

2 Answers2

0

Why not then use a nested serializer to represent the team?


class TeamSerializer(serializers.ModelSerializer):
    class Meta:
        model = Player
        fields = "__all__"

class PlayerSerializer(serializers.ModelSerializer):
    team = TeamSerializer(queryset=Team.objects.all())
    
    class Meta:
        model = Player
        fields = "__all__"

That will give you something like


{
"first_name": "John",
"last_name": "Doe",
"team": {
 "id": 1,
 "name": "First"
}
}

lullis
  • 304
  • 4
  • 7
-1

If there is a relationship between team and player, you should use the a serializer that represents the relationship.

class PlayerSerializer(serializers.ModelSerializer):
    team = serializers.PrimaryKeyRelatedField()
    
    class Meta:
        model = Player
        fields = "__all__"
lullis
  • 304
  • 4
  • 7
  • I did, please take a look at the attempt I described (the 2nd `serializers.py`, below links to questions I browsed before) – Gameplay Feb 21 '23 at 20:10
  • Also this doesn't work, needs to have queryset argument problvided, but even then the team is displayed as number, not the name (when fetching players and not teams) – Gameplay Feb 22 '23 at 08:05