1

The question in general is about finding the modification of a foreign key of a model and call some function of the related model.

Assume I have two model class:

class Discount(models.Model):
  def use(self, sell_item):
    if self.max_price:
        self.max_price -= sell_item.net()
    if self.max_count:
        self.max_count -= sell_item.amount

    self.save()

  def deuse(self, sell_item):
    if self.max_price:
        self.max_price += sell_item.net()
    if self.max_count:
        self.max_count += sell_item.amount

    self.save()

  max_price = models.PositiveIntegerField(blank=True,
                                          null=True)
  max_count = models.PositiveIntegerField(blank=True,
                                          null=True)
  amount = models.PositiveIntegerField(blank=False,
                                       null=False)


class SellItem(models.Model):
  def net(self):
    price = self.amount * self.price
    if self.discount:
      price -= self.discount.amount * price / 100
    return price * (1 + self.tax / 100)
  amount = models.PositiveIntegerField(balnk=False,
                                       null=False)
  price = models.PositiveIntegerField(blank=False,
                                      null=False)
  tax = models.PositiveIntegerFeidl(blank=False,
                                    null=False)
  discount = models.ForeignKey(Discount,
                               blank=True,
                               null=True)

Now I want to execute use function whenever a discount add to an item and deuse it whenever it is being removed from an item. I found a post about it and to do that I write below code for sell item:

def __init__(self, *args, **kwargs):
    self.dirty = False
    self.pre_states = []
    self.new_states = []
    super(SellItem, self).__init__(*args, **kwargs)

def __setattr__(self, name, value):
    if name == 'discount':
        if hasattr(self, name):
          pre_discount = self.discount
          if pre_discount != value:
            self.dirty = True
            if pre_discount:
                self.pre_states = ['pre_discount']
                self.pre_discount = pre_discount
            if value:
                self.new_states = ['discount']

    object.__setattr__(self, name, value)

def save(self, *args, **kwargs):
    super(SellItem, self).save(*args, **kwargs)

    if self.dirty:
        if 'pre_discount' in self.pre_states:
            self.pre_discount.deuse(self)
        if 'discount' in self.new_states:
            self.discount.use(self)

But it is not enough, because basically django would not fetch a foreign key when a new class is constructed, it instead just fill the _id item for it and whenever you need that it would fetch it from database, if I check for modification of discount_id instead of discount based on the order of setting of member values I may miss the previous discount because I have just current and previous discount_id not discount.

I know that it could possible implement with checking all of cases but I think after all I depend on django implementation of the behavior of database fetching which could be changed further.

I think there must be a proper and easier solution for just knowing the modification of a foreign key, I know there is some packages for storing history of modification but they are too much for my simple request.

motam
  • 677
  • 1
  • 6
  • 24
  • use pre_save/post_save signals and in receiver check if foreign key is being changed or not. – Vaibhav Vishal Sep 25 '18 at 09:27
  • You mean in `pre_save` the data in model is not updated and in `post_save` they were updated, so where is the data being saved and where is the previous data in those signals, I could not find any documentation for [signals](https://docs.djangoproject.com/en/2.1/ref/signals/#pre-save) to say something about the old or new states. – motam Sep 25 '18 at 09:38
  • use pre_save(), `update_fields` has all the fields that are going to get updated. Examples: https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html. I am not a big fan of signals and I avoid it, otherwise I would have posted an answer with complete code. – Vaibhav Vishal Sep 25 '18 at 09:45
  • I am familiar with signals and I used them before, `pre_save` and `post_save` are the same as that you run a code before calling parent save or after parent save in an override save as I have written in my example code. You example that you mention is not about these signals and just about signals in general. I checked this now and I get the same value before and after save as I supposed. – motam Sep 25 '18 at 11:35

0 Answers0