10

Here i have two models.In these models i want to make the value of amount_to_pay dynamic in Ledger model.For Example i have two different forms for these two models and while saving expense form if the user select the payment_option which comes from ledger model and gives some value for the amount_to_pay field then if only ledger.id and expense.payment_option_id are same then the value of amount_to_pay in ledger model should be replaced with that value.how can i do it ?

models.py

 class Expense(models.Model): pay_from = models.CharField(max_length=200) payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE) amount_to_pay = models.IntegerField(default=0) expense_date = models.DateField(default=datetime.date.today) expense_type = models.ForeignKey(ExpenseType, on_delete=models.CASCADE) note = models.TextField() created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) slug = AutoSlugField(unique_with='id', populate_from='expense_type') def get_amount_to_pay(self): return self.amount_to_pay class Ledger(models.Model): name = models.CharField(max_length=200) account_number = models.CharField(max_length=250, unique=True) account_type = models.CharField(max_length=200) opening_balance = models.IntegerField(default=0) amount_to_pay = models.IntegerField(default=0, blank=True, null=True) current_balance = models.IntegerField(default=0, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) slug = AutoSlugField(unique_with='id', populate_from='name') def save(self, *args, **kwargs): self.amount_to_pay = Expense.get_amount_to_pay(self) # here how can i save the amount_to_pay from expense form if the ledger.id and expense.payment_option.id matches?? #i got stuck here. self.current_balance = self.opening_balance - self.amount_to_pay super(Ledger, self).save(*args, **kwargs) 

4 Answers 4

12
+50

Solution One:

I think instead of changing in Ledger model, you should change in Expense model, like this:

class Expense(models.Model): ... def save(self, *args, **kwargs): self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay self.payment_option.save() super(Expense, self).save(*args, **kwargs) 

Solution Two:

But to be honest, Solution One does not seem good to me. Reason is that you are saving same data in 2 places(in both expense and ledger). Instead, it should be once, then the amount_to_pay value in Ledger should be calculated dynamically. Like this:

from django.db.models import Sum class Ledger(...): @property def amount_to_pay(self): # I am using a property method to show the amount_to_pay value. # FYI: in this way, you need to remove amount_to_pay field from Ledger model return self.opening_balance - self.expense_set.all().aggregate(a_sum=Sum('amount_to_pay')).get('a_sum', 0) 

In that way, with each ledger, the value amount_to_pay will be dynamically calculated at runtime. For example:

 for l in Ledger.objects.all(): l.amount_to_pay 

Solution Three:

If you are wary of making DB hits with each l.amount_to_pay(as it calculates amount_to_pay from DB dynamically) from previous solution, then you can always annotate the value. Like this:

For this solution, you need to change your Expense model and add a related_name:

class Expense(models.Model): pay_from = models.CharField(max_length=200) payment_option = models.ForeignKey('Ledger', on_delete=models.CASCADE, related_name='expenses')

Then use that related_name in query like this(FYI: You can't keep def amount_to_pay(...) method in Ledger model for the following usage example):

from django.db.models import Sum, F, ExpressionWrapper, IntegerField ledgers = Ledger.objects.all().annotate(expense_sum=Sum('expenses__amount_to_pay')).annotate(amount_to_pay=ExpressionWrapper(F('opening_balance') - F('expense_sum'), output_field=IntegerField())) # usage one for l in ledgers: l.amount_to_pay # usage two ledgers.values('amount_to_pay')
Sign up to request clarification or add additional context in comments.

Comments

0

Best thing is you override the call in save method any way you have foreginkey.

 def save(self, *args, **kwargs): #self.date_created = timezone.now() # YOUR LOGIC HERE super(YOUR_OVERRIDING_MODEL , self).save(*args, **kwargs 

3 Comments

amount_to_pay and current_balance is required
amount_to_pay = Expense.objects.get(id=form.cleaned_data['amount_to_pay_id']) print this value
amount_to_pay_id to amount_to_pay
0

check if Ledger have child and then update it:

class Expense(models.Model): def save(self, *args, **kwargs): self.payment_option.amount_to_pay = self.payment_option.amount_to_pay + self.amount_to_pay self.payment_option.save() super(Expense, self).save(*args, **kwargs) 

2 Comments

self.ledger_payment_option.amount_to_pay = 1000 what it does ?? i want to save it dynamic .if only the user gives value from expense modelform and the id of both payment_option and ledger matches then the value of amount_to_pay in ledger table should be replaced with that entered data by user
if you wan to update Ledger.amount_to_pay from Expense model you have answer.
-1

Override save method and update some field dynamically in django

For my case i wanted to update done column in answer model dynamically when i comment on an answer it should return true instead of False. So in my example i am checking for whether that column done which is boolean is False and if it's False,i change it's status to True using the answer object from Comment and then save the answer object, see more about update vs insert fields https://docs.djangoproject.com/en/3.2/ref/models/instances/

class Answer(models.Model): user = models.ForeignKey("User", on_delete=models.CASCADE, blank=True, null=True) username = models.CharField(max_length=250) codeAnswer = models.CharField(max_length=250) girAnswer = models.CharField(max_length=250) correct = models.BooleanField(default=False) firebaseToken = models.CharField(max_length=250) done = models.BooleanField(default=False) created_at = models.DateTimeField() updated_at = models.DateTimeField(auto_now=True) def __unicode__(self): return "{0}: {1}".format(self.username, self.value) def __str__(self): return "%s" % (self.codeAnswer) # So here i override the save method in the Comment model to do what i want as below. class Comment(models.Model): answer = models.ForeignKey("Answer", on_delete=models.CASCADE, blank=False , related_name="comments") writerName = models.CharField(max_length=250) content = models.CharField(max_length=250) deleted = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return "%s" % (self.writerName) def save(self, *args, **kwargs): if self.answer.done is False: self.answer.done = True self.answer.save() super(Comment, self).save(*args, **kwargs) 

Hope it answers the question above thanks

2 Comments

If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.