I have a django project with several models. Two of this models are:
class Attack(models.Model):
spell = models.ForeignKey("Spell", models.CASCADE, null=True)
attacker = models.ForeignKey("Ent", models.CASCADE, related_name='attacker')
defender = models.ForeignKey("Ent", models.CASCADE, related_name='defender')
defender_pts_before = models.IntegerField(default=0, blank=False, null=False)
damage_caused = models.IntegerField(default=0, blank=False, null=False)
and
class Ent(models.Model):
name = models.CharField(max_length=50, blank=False, null=False)
raze = models.CharField(max_length=50, blank=False, null=False)
damage = models.CharField(max_length=50, blank=False, null=False)
weakness = models.CharField(max_length=50, blank=False, null=False)
health = models.FloatField(default=100, blank=False, null=False)
attacks = models.ManyToManyField('self', through=Attack, symmetrical=False, related_name='attacked_from')
battles = models.ManyToManyField(Battle, through=BattleParticipant, related_name='battles_part')
As you can see the Ent
model has a m2m field to itself through the Attack
model. This is for representing the attacks between ents. The attack model stores all the damage the attacker caused to the defender in the damage_caused
field (which is calculated when the attack is saved).
I need to know for each ent the total damage it has caused in all the attacks, so I built the following query:
Ent.objects.all().annotate(dmg=Sum('attacks__attacker__damage_caused'))
but this doesn't seems to work. It only seems to work the right way with this query:
Ent.objects.all().annotate(dmg=Sum('attacks__defender__damage_caused'))
Tests
I've created some entries to check the query (a single attack from one ent to another)
In [106]: e1, e2 = Ent.objects.all().annotate(dmg=Sum('attacks__attacker__damage_caused'))[:2]
In [107]: e1, e2
Out[107]: (<Ent: Ent object (240)>, <Ent: Ent object (241)>)
In [108]: e1.attacks.all()
Out[108]: <QuerySet [<Ent: Ent object (241)>]>
In [109]: e2.attacks.all()
Out[109]: <QuerySet []>
As you can see e1
only has one attack, and e2
doesn't have any. Let's see this attack:
In [110]: atk = Attack.objects.filter(attacker=e1).first()
In [111]: atk.attacker
Out[111]: <Ent: Ent object (240)>
In [112]: atk.defender
Out[112]: <Ent: Ent object (241)>
In [113]: atk.damage_caused
Out[113]: 20
All right, the attacker is e1
and the defender is e2
, but when I check the annotate values:
In [114]: e1.dmg, e2.dmg
Out[114]: (None, None)
e1
should had 20 in the total damage but both are None. The strange part comes when I do the following query:
In [115]: e1, e2 = Ent.objects.all().annotate(dmg=Sum('attacks__defender__damage_caused'))[:2]
In [116]: e1, e2
Out[116]: (<Ent: Ent object (240)>, <Ent: Ent object (241)>)
In [117]: e1.dmg, e2.dmg
Out[117]: (20, None)
If I access to the attack by defender
(instead of attacker
) it gives me the right answer. So I'm a little confuse here. Why does this happen this way?