8

Is it possible to detect that field in Django model has been changed?

class Product(models.Model):
    my_current_price = MoneyField(max_digits=20, decimal_places=2, null=True, blank=True,
                                  verbose_name=_('My original price'))
    my_eur_price = MoneyField(max_digits=20, decimal_places=2, null=True, blank=True, verbose_name=_('My price in EUR'))
    my_usd_price = MoneyField(max_digits=20, decimal_places=2, null=True, blank=True, verbose_name=_('My price in USD'))

The thing is that I need to recalculate EUR and USD price anytime when my_current_price is changed.

I have two ideas:

  1. Somehow override MoneyField and send signal when the field is changed.

  2. Override save method and create a new attribute __my_current_price like here - this works but it makes code very unclear for me.

EDIT: I store the price in different currencies because of faster database lookups.

Milano
  • 18,048
  • 37
  • 153
  • 353
  • yes use signal for post_save, when current_price will be changed according to it, the other 2 fields will be calculated and saved – Exprator Jul 05 '17 at 07:43
  • How can I then detect that current_price is changed? – Milano Jul 05 '17 at 07:44
  • @Exprator Oh yes, you mean this answer? https://stackoverflow.com/a/7934958/3371056 It seems good. – Milano Jul 05 '17 at 07:46
  • yes thats what signal i was talking about :) – Exprator Jul 05 '17 at 07:48
  • Although this is a good way, I'm thinking about using pre_save - is it possible? With pre_save, there would be just one commit. I'm not sure if I can access objects not commited changes in pre save. – Milano Jul 05 '17 at 07:59
  • its better to use in post_save as you will get the actual value as the signal will confirm the value has been saved – Exprator Jul 05 '17 at 08:01

1 Answers1

15

One way is to create a signal and compare instance with an object from database. But it seems that better way is to override save method which is a best practice.

def save(self,*args,**kwargs):
    old = Model.objects.filter(pk=getattr(self,pk,None)).first()
    if old:
        if old.attr!=self.attr:
            # attr changed
    super(Model,self).save(*args,**kwargs)
Věroš K.
  • 103
  • 3
Milano
  • 18,048
  • 37
  • 153
  • 353
  • 2
    QuerySets are lazy, using them as a boolean value will execute a corresponding query in the database. Instead remove the `.first()` and check the existence of the instance by doing `old.exists()` – Cyzanfar Apr 19 '22 at 14:08