Chidananda Nayak
Chidananda Nayak

Reputation: 1201

How to manipulate value of one Model Field from another Model?

I have two models

class Employee(models.Model):
    name = models.CharField(max_length=20)
    ID = models.IntegerField()
    basic_salary = models.IntegerField()     
    total_leave = models.IntegerField(default = 14)
    paid_leave = models.IntegerField(default = 0)
    unpaid_leave = models.IntegerField(default = 0)

def __str__(self):
    return self.name

class Leave_management(models.Model):

    name = models.OnetoOneField(Employee,on_delete= models.CASCADE)
    reason = models.CharField(max_length=50)
    from = models.DateTimeField()
    to = models.DateTimeField()
    total_days = models.IntegerField()

     def __str__(self):
        return self.name

So,i want to minus 'total_days' of 'model-Leave_management' from 'total_leave' field of 'model-Employee'. And as per leaves taken i want to update 'paid_leave' and 'unpaid_leave' sections.

I can perform so if these two models would be one model(example below), But i dont know how to perform so in different models.

def save(self,*args,**kwargs):
    if self.total_days<=self.total_leave:
        self.total_leave -= self.total_days
        self.unpaid_leave = 14 - self.total_leave
   else:
       self.total_days -= 14
       self.paid_leaves = self.total_days
   super(Model_name,self).save(*args,**kwargs)

` Please be guiding me.

Upvotes: 4

Views: 1529

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477676

In fact your OneToOneField(..) to an Employee is not a name. At the database level it will store values that correspond to primary keys of an Employee, and in Django, name will be a lazy fetch to the corresponding Employee. Therefore I suggest to rename your function to (for example) employee.

Another problem is that you define it as a OneToOneField. That means that an Employee has one Leave_management. But based on the fields (reason, from, to, etc.), it looks like an Employee can have zero, one, or more Leave_managements. So that means it is a ForeignKey.

So our model looks like:

class Leave_management(models.Model):

    employee = models.ForeignKey(Employee,on_delete= models.CASCADE)
    reason = models.CharField(max_length=50)
    from = models.DateTimeField()
    to = models.DateTimeField()
    total_days = models.IntegerField()

     def __str__(self):
        return self.employee.name

Like the __str__ function already suggests, we can obtain the name of the employee by querying self.employee, and we can then fetch its .name attribute.

But now the challenge is what to do when save a Leave_management object. In that case the number of total_leave and paid_leave should be updated.

We first have to figure out the total number of total_days that are stored in Leave_management objects related to an Employee, this is equal to:

(Leave_management.objects.filter(employee=some_employee)
                         .aggregate(totals=Sum('total_days'))['totals'] or 0

So we can then subtract this from 14, and store the (possibly) remaining days in paid_leave, like:

class Leave_management(models.Model):

    # ...

    def save(self, *args, **kwargs):
        super(Leave_management, self).save(*args, **kwargs)
        totals = (Leave_management.objects
                                  .filter(employee=some_employee)
                                  .aggregate(totals=Sum('total_days'))['totals'] or 0
        employee = self.employee
        unpaid = min(14, totals)
        employee.total_leave = 14 - unpaid
        employee.unpaid_leave = unpaid
        employee.paid_leave = totals - unpaid
        employee.save()

Note: typically we do not handle this by overriding the .save(..) function, but by using Django signals: triggers that can be implemented when certain objects are saved, etc. This especially should be used since the objects can be changed circumventing the .save(..) function, and sometimes such objects might get deleted as well. So the above is not a good design decision.

Even when we use signals, it is a good idea to frequently (for example once a day) recalculate the total leave, and update the corresponding Employee models.

Upvotes: 2

Related Questions