Django обновляет объекты m2m при сохранении
У меня есть rel m-2-m и я хочу обновить rel B после добавления любого rel A через страницу администратора.
Вот подробности:
У меня есть 2 модели, Матч (rel A) и Игрок (rel B). Я хочу обновлять Player.matches_played каждый раз, когда добавляется новый матч с этим игроком в Match.team1 или Match.team2.
Как я могу этого добиться???
модели
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)
Я пробовал сигналы, но в post_save сигнал {instance.team1} или instance.team1.all() возвращают пустой QuerySet, что я считаю правильным, так как M2M rel сохраняется позже. Затем я попробовал m2m_changed, но этот сигнал не срабатывает при сохранении через страницу администратора. Что я упускаю?
@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)
Большое спасибо за помощь
альтернатива, о которой я думал, это получать данные через свойство в Player, например, так, но я думаю, с точки зрения производительности, лучше обновлять строки каждый раз, когда добавляется матч, а затем считать матчи каждый раз, когда запрашивается игрок
Пример
@property
def matches_played(self):
return Match.objects.filter(team1__nome=self.nome).count()+Match.objects.filter(team2__nome=self.nome).count()
Отправителем сигнала m2m_change
[Django-doc] является не Match
, а Match.team1.through
, и/или Match.team2.through
, поэтому:
@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)
При этом хранить количество сыгранных матчей в качестве поля в модели не имеет особого смысла. Мы можем определить его при необходимости с помощью:
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.