How to update values ​​in an instance when they have a custom .update () to update many-to-many relationships in a nested DRF record serializer

I have three models of the player, team, and membership, where the player and team have a many-to-many relationship, using membership as an intermediate model.

class Player(models.Model): name = models.CharField(max_length=254) rating = models.FloatField(null=True) install_ts = models.DateTimeField(auto_now_add=True, blank=True) update_ts = models.DateTimeField(auto_now_add=True, blank=True) class Team(models.Model): name = models.CharField(max_length=254) rating = models.FloatField(null=True) players = models.ManyToManyField( Player, through='Membership', through_fields=('team', 'player')) is_active = models.BooleanField(default=True) install_ts = models.DateTimeField(auto_now_add=True, blank=True) update_ts = models.DateTimeField(auto_now_add=True, blank=True) class Membership(models.Model): team = models.ForeignKey('Team') player = models.ForeignKey('Player') #date_of_joining = models.DateTimeField() install_ts = models.DateTimeField(auto_now_add=True, blank=True) update_ts = models.DateTimeField(auto_now_add=True, blank=True) 

Now I needed to renew this membership using the django rest framework. I tried updating them using Writable nested serializers by writing a custom .update() command serializer.

 @transaction.atomic def update(self, instance, validated_data): ''' Cutomize the update function for the serializer to update the related_field values. ''' if 'memberships' in validated_data: instance = self._update_membership(instance, validated_data) # remove memberships key from validated_data to use update method of # base serializer class to update model fields validated_data.pop('memberships', None) return super(TeamSerializer, self).update(instance, validated_data) def _update_membership(self, instance, validated_data): ''' Update membership data for a team. ''' memberships = self.initial_data.get('memberships') if isinstance(membership, list) and len(memberships) >= 1: # make a set of incoming membership incoming_player_ids = set() try: for member in memberships: incoming_player_ids.add(member['id']) except: raise serializers.ValidationError( 'id is required field in memberships objects.' ) Membership.objects.filter( team_id=instance.id ).delete() # add merchant member mappings Membership.objects.bulk_create( [ Membership( team_id=instance.id, player_id=player ) for player in incoming_player_ids ] ) return instance else: raise serializers.ValidationError( 'memberships is not a list of objects' ) 

Now this works well for updating the values ​​in the database for the membership table. The only problem I am facing is that I cannot update the preloaded instance in memory, which, at the request of PATCH for this API, updates the values ​​in the database, but the API response shows stale data.

The following GET request for the same resource gives updated data. Anyone who has worked with the many-to-many relationship in django and written their own update / create methods for writeable nested serializers can help me understand a possible way to solve this problem.

+7
django django-orm django-rest-framework
source share
2 answers

In this case, I was able to find the issue . I used .prefetch_related() in a query that calls an instance to use preselected data for many, many relationships. I have two solutions that can cause a few more database calls to get updated data.

1. Do not use .prefetch_related()

One obvious way is to not use .prefetch_related() which is not recommended , as this will lead to a large number of database deletions.

OR

2. Getting an updated instance of the model after updating the many-to-many relationships in the serializer update method

 instance = self.context['view'].get_queryset().get( id=instance.id ) 

Changed .update() of TeamSerializer

 @transaction.atomic def update(self, instance, validated_data): ''' Cutomize the update function for the serializer to update the related_field values. ''' if 'memberships' in validated_data: instance = self._update_membership(instance, validated_data) # remove memberships key from validated_data to use update method of # base serializer class to update model fields validated_data.pop('memberships', None) # fetch updated model instance after updating many-to-many relations instance = self.context['view'].get_queryset().get( id=instance.id ) return super(TeamSerializer, self).update(instance, validated_data) 

If anyone has a better approach, I would appreciate it if they could add an answer to this question.

+1
source share

I think you need to "reload" the players field using instance.players.all() , because it is already evaluated in the serializer's is_valid method.

 @transaction.atomic def update(self, instance, validated_data): ''' Cutomize the update function for the serializer to update the related_field values. ''' if 'memberships' in validated_data: instance = self._update_membership(instance, validated_data) # remove memberships key from validated_data to use update method of # base serializer class to update model fields validated_data.pop('memberships', None) instance.players.all() return super(TeamSerializer, self).update(instance, validated_data) 
+2
source share

All Articles