Take the folllowing simplified models
class Bill(models.Model):
name = models.CharField(max_length=100)
total_amount = models.DecimalField()
class Payment(models.Model):
service = models.ForeignKey(Bill, on_delete=models.CASCADE, related_name='payments')
amount = models.IntegerField()
It is essential for retaining data integrity that the sum of a Bill’s Payments never exceed the BIlls’ total amount.
I tried to implement this validation rule within the Bill clean method:
class Bill(models.Model):
name = models.CharField(max_length=100)
total_amount = models.DecimalField()
def clean(self):
super.clean()
if sum([p.amount for p in self.payments.all()]) > self.total_amount:
raise ValidationError("Payments exceed bill amount")
But I am having trouble since the set of related Payments will not always be accessible to the Bill instance if the Payment instances have not been commited to the database. (for example, when creating both Bill and Payments from a BillAdmin with PaymentInlines).
I found that I could perform this validation using a FormSet, but if I ever forget to use this FormSet the data integrity could be compromised.
Also I found that I could use signals to trigger validation after the objects have been commited to the database, but I would have to deal with deleting the objects later if they are not valid.
I feel like this is a very vanilla use case yet I am struggling to find a simple solution.
What am I missing here? Is it a wrong approach to try to implement this kind of validation within the Bill model?