0

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?

Jorge Morgado
  • 1,148
  • 7
  • 23
  • Hello @Jorge Morgado i think you have some logical error i will suggest to check this post https://stackoverflow.com/a/11723808/14457833 – Ankit Tiwari May 20 '21 at 18:33
  • Hi, sorry but I can't see how this problem is related to that post. I have the m2m field already using `'self'` (as the post you cite). Am I missing something? – Jorge Morgado May 20 '21 at 22:29

0 Answers0