0

This is models.py

class ledger1(models.Model):
    User = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
    Company = models.ForeignKey(company,on_delete=models.CASCADE,null=True,blank=True,related_name='Companys')
    Creation_Date = models.DateField(default=datetime.now)
    name = models.CharField(max_length=32,unique=True)
    Opening_Balance = models.DecimalField(max_digits=19,decimal_places=2)
    Closing_balance = models.DecimalField(max_digits=10,decimal_places=2)


class journal(models.Model):
    User = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
    Company = models.ForeignKey(company,on_delete=models.CASCADE,null=True,blank=True,related_name='Companyname')
    Date = models.DateField()
    By = models.ForeignKey(ledger1,on_delete=models.CASCADE,related_name='Debitledgers')
    To = models.ForeignKey(ledger1,on_delete=models.CASCADE,related_name='Creditledgers')
    Debit = models.DecimalField(max_digits=10,decimal_places=2,)
    Credit = models.DecimalField(max_digits=10,decimal_places=2)

I want to make a mathamatical equation using django queries and the result of my equation will be displayed in the ledger1.Closing_Balance field

So I have tried this:

@receiver(pre_save, sender=ledger1)
def update_user_closing_balance(sender,instance,*args,**kwargs):
    Closing_balance = ledger1.objects.annotate(debitsum=Sum('Debitledgers__Debit')) + instance.Opening_Balance - ledger1.objects.annotate(creditsum=Sum('Creditledgers__Credit'))
    instance.Closing_balance = Closing_balance

Is it possible in django??? Because if I run this I am getting unsupported operand error...

Is there any alternative code for this???

If anyone knows it...Plz help

Thank you in advance

Niladry Kar
  • 1,163
  • 4
  • 20
  • 50

1 Answers1

3

The unsupported operand exception has nothing to do with the query itself, but with the expression you wrote:

Closing_balance = (
   ledger1.objects.annotate(debitsum=Sum('Debitledgers__Debit')) +
   instance.Opening_Balance -
   ledger1.objects.annotate(creditsum=Sum('Creditledgers__Credit'))
)

so here you are adding QuerySets together with real values, and that does not make any sense. What you probably wanted to use was an .aggregate(..), and then wrap the value it contains out of it, like:

debitsum = ledger1.objects.aggregate(debitsum=Sum('Debitledgers__Debit'))['debitsum']
creditsum = ledger1.objects.aggregate(creditsum=Sum('Creditledgers__Credit'))['creditsum']

Closing_balance = debitsum + instance.Opening_Balance - creditsum

But that being said, using signals to pre-calculate aggregates is typically not a good idea. Since for example a journal object can change its Debit or Credit value, and this will not "fire" the signal, hence no update is done. Even if you also add logic for such events, it can still happen that the signals do not fire, since for example updating in bulk will surpass the signaling system.

Usually it is better not to store data aggregates, since this leads to data duplication which, like the article says "breeds redundancy and inconsistency.". If you want to calculate such aggregates, it might be better to use a (materialized) view at the database level.

EDIT: the query itself however does not seem to make any sense. If you update a ledger record, you can perform a filtering, and calculate the update that way:

@receiver(pre_save, sender=ledger1)
def update_user_closing_balance(sender,instance,*args,**kwargs):
    debit = instance.Debitledgers.aggregate(debit=Sum('Debit'))['debit']
    credit = instance.Creditledgers.aggregate(credit=Sum('Credit'))['credit']
    instance.Closing_balance = instance.Opening_Balance + debit - credit

But this probably will still not suffice, since you will then need to do some proper filtering on the date, etc.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • No actually I want diffrent closing balances for diffrent ledgers...If I use aggregate It will be same for all ledgers...Is there any possibilities to perform this? – Niladry Kar Sep 22 '18 at 07:41
  • @NiladryKar: say that are different `ledger`s, then how are you supposed to save it an **`instance`**? A `pre_save`, and `post_save` always works on *one* instance. – Willem Van Onsem Sep 22 '18 at 07:44
  • Oooo yes totally forgot that..Can you provide me another way to perform this???@Willem Van Onsem – Niladry Kar Sep 22 '18 at 07:46
  • @NiladryKar: well somehow you always end up with the **XY** problem, instead of explaining *how* you want to solve something, try to explain *what* you want to solve. – Willem Van Onsem Sep 22 '18 at 07:48
  • @NiladryKar: the following *might* do the trick, but as said in the article, this is simply an anti-pattern. Data duplication has resulted huge software disasters (like explained in the article). So it is simply not a good idea to introduce such data duplication (yourself). – Willem Van Onsem Sep 22 '18 at 07:54
  • 1
    Thank you very much@Willem Van Onsem...Yeah I know that this is just half completed...Very much thanks to you for providing this to me this is just what I was looking for... – Niladry Kar Sep 22 '18 at 07:59
  • I have 1 more doubt if you can clear it for me...What will I change in the receiver function If I change the sender to journal??? – Niladry Kar Sep 22 '18 at 13:38