I have a m-2-m rel and i want to update the rel B after any rel A is added via the admin page.
Here the details:
I have 2 models, Match (rel A) and Player (rel B). I want to update Player.matches_played eveytime a new Match with that player in Match.team1 or Match.team2 is added.
How can i achieve this???
the models
class Match(models.Model):
data=models.DateField(auto_now_add=False,auto_now=False)
team1=models.ManyToManyField('players.Player',related_name='team_1')
team2=models.ManyToManyField('players.Player',related_name='team_2')
score_team1=models.IntegerField(validators=[MinValueValidator(0)])
score_team2=models.IntegerField(validators=[MinValueValidator(0)])
class Player(models.Model):
matches_played= models.IntegerField(validators=[MinValueValidator(0)], default=0)
I tried signals but in post_save signal {instance.team1} or instance.team1.all() return an empty QuerySet which i believe is correct as the M2M rel is saved later. Then i tried m2m_changed but that signal is not fired with a save via the admin page. What am i missing?
@receiver(post_save, sender=Match, dispatch_uid="update_player_game_count")
def update_player_games(sender, instance, created, **kwargs):
print(f'created {instance.pk}')
print(f'created {instance.team1}')
print(f'created {instance.team2}')
print(f'created {instance.score_team1}')
print(instance.team1.all())
@receiver(m2m_changed, sender=Match, dispatch_uid="update_player_game_count")
def update_player_game(sender, instance, created, action, **kwargs):
print(action)
if action == 'post_add':
print(action)
Thank you very much for you help
an alternative which i thought about is to retrieve the data via a property in Player for instance like this, but i think, from a performance point of view, is better to update rows every time a match is added then count matches everytime a player is requested
Example
@property
def matches_played(self):
return Match.objects.filter(team1__nome=self.nome).count()+Match.objects.filter(team2__nome=self.nome).count()
The sender of the m2m_change
signal [Django-doc] is not Match
, but Match.team1.through
, and/or Match.team2.through
, so:
@receiver(
m2m_changed,
sender=Match.team1.through,
dispatch_uid='update_player_game_count1',
)
@receiver(
m2m_changed,
sender=Match.team2.through,
dispatch_uid='update_player_game_count2',
)
def update_player_game(sender, instance, created, action, **kwargs):
print(action)
if action == 'post_add':
print(action)
That being said, it make not much sense to store the number of matched played as a field in the model. We can determine it when needed, with:
from django.db.models import Count
Player.objects.annotate(
total_matches=Count('team1', distinct=True) + Count('team2', distinct=True)
)
Furthermore I don't think using two ManyToManyField
s is per se the best modeling here. You might want to use a single ManyToManyField
with a through=…
model [Django-doc] that determines if the player played for the first or second team.