'Django Serialization - Partial update on nested objects

Searched around for a few hours on this and I am surprised I couldn't find an answer but here it goes.

Let's say I have the following models:

class Mission(models.Model):
    mission_name = models.CharField(max_length=150)
    ...

class Player(models.Model):
    player_name = models.CharField(max_length=150, unique = True)
    state = models.CharField(max_length=150)
    currentMission = models.ForeignKey(Mission,on_delete=models.SET_NULL, blank=True, null=True)) 

Objectives:

  1. When creating a mission, I would like to provide the players' names that are going to participate on this mission (Names are unique). That means, when mission is created, I have to update the currentMission field of each given player. (Players already exist when mission is created)
  2. When I try to GET a mission, I would like to see the names of the players that participate

My attempt

Class MissionSerializer(serializers.ModelSerializer):
    
    #This is to get a list of the players that participate in this mission
    players = PlayerSerializer(many=True,  read_only=True) 
    class Meta:
        model= Mission
        fields = ['mission_name','players']


    def create(self,validated_data):
        mission = Mission.objects.create(**validated_data)
                
                
        # Notice that I get the "players" data from "self.initial_data" and not from validated_data
        # This is because validated_data does not have the "players" field,
        # (I guess because I didn't provide all the required fields on my request for the player. I just provided the players' names )
        players_data = self.initial_data['players']

        #Update each player's current mission
        for player_data in players_data:
            qs = Player.objects.filter(player_name=player_data['player_name'])
            obj = get_object_or_404(qs)
            serializer = PlayerSerializer(obj, data={'currentMission': mission.id}, partial=True)
            if (serializer.is_valid()):
                serializer.save()


class PlayerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Player
        fields = ('__all__')

    def update(self, instance, validated_data):
        instance.currentMission = validated_data['currentMission']
        instance.save()
        return instance

The above works for objective #1. However, it does not work for objective #2. That is, when I GET a mission, the list of players is not present on the result.

My question is, how could I also retrieve this list when performing GET requests?



Solution 1:[1]

I think that players field lacks a source. Adding a source to the field should solve your problem.

players = PlayerSerializer(many=True,  read_only=True, source='player_set')

Also, I'd recommend to prefetch that players to optimize the database query.

In your view;

mission_queryset = Mission.objects.filter(...).prefetch_related('player_set')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cagatay Barin