Reputation: 21
I have two models, an Invoice model and a LineItem model. The LineItem model looks like this:
class LineItem(models.Model):
unit_price = models.DecimalField()
quantity = models.IntegerField()
invoice = models.ForeignKey(Invoice)
@property
def lineitem_total(self): return self.unit_price * self.quantity
The Invoice model also has a total
property, which returns the sum of the total of all of the related line items.
Now, when the line items related to an invoice get updated, I need to validate if the total
property on the Invoice exceeds a certain maximum value. However the clean()
method on the Invoice fires before the related line items get updated, so it still returns the old value. I need the validation to happen on the model itself rather than a form.
Is there a way to validate the line items?
I've tried putting the validation in the Invoice
model's clean()
method, however the total
property still returns the old value before the line items are updated.
I've also tried raising a ValidationError
in the Invoice
model's save()
method, however that returns a 500
error.
Upvotes: 1
Views: 28
Reputation: 52018
You can try like this in the LineItem
model form (explanation in code comments):
class SomeFormItem(forms.ModelForm):
...
def save(self, commit=False):
instance = super().save(commit=False)
total = instance.invoice.total # get total of line items (assuming used using reverse query)
if self.instance.pk and ('quantity' in self.changed_data or 'unit_price' in self.changed_data): # if we are editing already existing lineitem and total has been changed
total += (self.cleaned_data['unit_price'] - self.data['unit_price']) * (self.cleaned_data['quantity'] - self.data['quantity']) # self.cleaned_data contains new information and self.data contains old information and calculating the difference only
else:
total += self.cleaned_data['quantity'] * self.cleaned_data['unit_price']
if total > SOME_NUMBER:
raise ValidationError()
return super().save(commit=commit)
More information can be found in the documentation.
Upvotes: 1