danish2694
danish2694

Reputation: 269

update django models column based on other column

I have a model like

class tbl_payment(models.Model):
    document_id                     = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
    client_id                       = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
    total_amount                    = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    paid_amount                     = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    balance                         = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    date                            = models.DateField(blank=True, null=True)
    status                          = models.CharField(max_length=50)

Now what I want to do is whenever a new record is added, or an existing record changes, balance should be updated as the difference between total_amount and paid_amount (simple maths), and based on my balance column, I want to save status as Paid, Partial or Unpaid.

I want to refrain from calculating the balance in my views and then saving in the database, instead, I want to handover this part to my models so that my models take care of the balance and I may avoid errors which I am subconsciously afraid of.

I came to know that this is done something like this

class tbl_payment(models.Model):
    document_id                     = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
    client_id                       = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
    total_amount                    = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    paid_amount                     = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    balance                         = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    date                            = models.DateField(blank=True, null=True)
    status                          = models.CharField(max_length=50)

    @property
    def balance(self, value):
        return self.total_amount - paid_amount
        

but what should I pass in place of value?? also when I try to get due_amount value like this

tbl_bulk_payment.objects.get(pk=1).due_amount

it gives due_amount() missing 1 required positional argument: 'value'

what is the correct way of doing this??

Upvotes: 1

Views: 2240

Answers (1)

kochul
kochul

Reputation: 681

you have to override clean() function. https://docs.djangoproject.com/en/4.2/ref/models/instances/#django.db.models.Model.clean

Model.clean() ... to modify attributes on your model ...

    class tbl_payment(models.Model):
        class Status(models.TextChoices):
            paid = 'Paid', 'Paid'
            partial = 'Partial', 'Partial'
            unpaid = 'Unpaid', 'Unpaid'
            
        document_id                     = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
        client_id                       = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
        total_amount                    = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
        paid_amount                     = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
        balance                         = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
        date                            = models.DateField(blank=True, null=True)
        status                          = models.CharField(max_length=50, choices=Status.choices)
    
        def clean(self):
            self.balance = self.total_amount - self.paid_amount
            
            if self.balance >= 0:
                self.status = Status.paid
            elif self.paid_amount == 0:
                self.status = Status.unpaid
            else:
                self.status = Status.partial

P.S. your Model class name is not following the python or Django naming rule. class Payment(models.Model) would be better.

and document_id => document. because

payment = Payment.objects.get(pk=1)
payment.document_id

in this case payment.document_id would return document instance. (not document instance's id). so document is more Django like style than document_id

Upvotes: 3

Related Questions