Django - update model field based on another field
I am new to Django and Python and I want to do what I have done very often in Java EE.
Consider the following model (only relevant classes):
class Item(models.Model):
name = models.CharField(max_length=40)
default_price = models.DecimalField(max_digits=6, decimal_places=2, default=50)
def __unicode__(self):
return self.name
class SaleDetail(models.Model):
item = models.ForeignKey(Item)
deposit = models.ForeignKey(Deposit)
quantity = models.PositiveIntegerField()
unit_price = models.DecimalField(max_digits=6, decimal_places=2)
sale = models.ForeignKey(Sale)
def item_updated(self, value):
if self.unit_price == None:
self.unit_price = value
What I want to do is that every time it is Item
added Item
it is updated SaleDetail.unit_price
with Item.default_price
if SaleDetail
is new or not unit_price
.
What I was doing in Java POJO was to include this logic in the setter method. I tried using python properties to encapsulate the property Item
, but Django updates the field directly under the hood, so this breaks some of the automatic functionality.
I also tried to subclass ForeignKey to accept a callback function, but couldn't find a way to call the method in the container class.
I want to do this so that I can provide a default value for the UI, but I do not want to include this logic in the presentation logic as conceptually I think this logic should be on the model (server side)
Another use in this case would be to update totals for each sale and sale detail. I would like to calculate this before the user decides to save the sell, so there is no way to save the signals.
Thank!
source to share
The "unit price" is literally a function of two different fields. So I would like to write it like this:
class Item(models.Model):
name = models.CharField(max_length=40)
default_price = models.DecimalField(max_digits=6, decimal_places=2, default=50)
def __unicode__(self):
return self.name
class SaleDetail(models.Model):
item = models.ForeignKey(Item)
deposit = models.ForeignKey(Deposit)
quantity = models.PositiveIntegerField()
entered_unit_price = models.DecimalField(max_digits=6, decimal_places=2, default=None)
sale = models.ForeignKey(Sale)
@property
def unit_price(self, value):
if self.entered_unit_price is None:
return self.item.default_price
else:
return self.entered_unit_price
@unit_price.setter
def unit_price(self, value):
self.entered_unit_price = value
Then use it like this:
print(sd.unit_price)
sd.unit_price = 500
sd.save()
source to share
See if this works for you. We override the save method and check if there is pk
. If pk
- None, we know what SaleDetail
is new. Then we see if it exists unit_price
. If not unit_price
, we set it to Item.default_price
.
class Item(models.Model):
name = models.CharField(max_length=40)
default_price = models.DecimalField(max_digits=6, decimal_places=2, default=50)
def __unicode__(self):
return self.name
class SaleDetail(models.Model):
item = models.ForeignKey(Item)
deposit = models.ForeignKey(Deposit)
quantity = models.PositiveIntegerField()
unit_price = models.DecimalField(max_digits=6, decimal_places=2)
sale = models.ForeignKey(Sale)
def save(self, *args, **kwargs):
# Make sure this is the first save (pk should be None) and there is no unit_price set
if self.pk is None and not self.unit_price:
self.unit_price = self.item.default_price
elif not self.unit_price:
self.unit_price = self.item.default_price
# Call the original save method
super(SaleDetail, self).save(*args, **kwargs)
source to share