Django Model Method or Calculation as Field in Database

Ron Raney picture Ron Raney · Jun 28, 2017 · Viewed 9.5k times · Source

Using Django ~=1.11 and Python 3.6

I need to store 'calculated' variables as fields in the Django model database.

Here's a model:

from django.db import models
from datetime import date

class Person(model.Model)
    "Last Name"
    last_name = models.CharField(max_length=25)

    "Birthday"
    birth_date = models.DateField()

    "City of birth"
    city_of_birth = models.CharField(max_length=25)

I am creating a Unique ID using these fields. Specifically, I'm conjoining parts of each field into one string variable (details below). I was able to get this to work as a Property but I don't know how to store a calculated field in the database.

"Unique ID"
def get_id(self):
    a = self.last_name[:2].upper()     #First 2 letters of last name
    b = self.birth_date.strftime('%d')     #Day of the month as string
    c = self.city_of_birth[:2].upper()     #First 2 letters of city
    return a + b + c 
unique_id = property(get_id)

I want to do a similar thing with Age. Here's what I have as a calculation:

"Age calculated from Birth Date"
def get_age(self):
    return int((datetime.date.now() - self.birth_date.days) / 365.25)
age = property(get_age)

So I'd like to store the UniqueID and Age variables in the database, as fields in the Person model. What is the best practice when doing these? Do I need to initialize the fields first, then do some sort of update query to these?

Note: It is my understanding that the current code using 'property' works for rendering in the view, but it is not stored in the database.

Thanks in advance! Please help me improve what I already have.

UPDATE: Here is code that worked for me. The problem was that I needed to drop the parentheses in the save() section, after self.unique_id=self.get_unique_id . It has been suggested to drop age from the database, and leave it as a property.

class Person(models.Model):
    unique_id = models.CharField(max_length=6, blank=True)
    last_name = models.CharField(max_length=25)
    birth_date = models.DateField()
    city_of_birth = models.CharField(max_length=25)

    @property
    def get_unique_id(self):
        a = self.last_name[:2].upper()     #First 2 letters of last name
        b = self.birth_date.strftime('%d')     #Day of the month as string
        c = self.city_of_birth[:2].upper()     #First 2 letters of city
        return a + b + c 

    @property
    def age(self):
        return relativedelta(self.birth_date.days, datetime.date.now()).years

    def save(self, *args, **kwarg):
        self.unique_id = self.get_unique_id
        super(Person, self).save(*args, **kwarg)

    def __str__(self):
        return self.unique_id

Answer

josemlp picture josemlp · Jun 28, 2017

You have to override the save method of yout Model Person and create unique_id and age field in the Model.

from dateutil.relativedelta import relativedelta
from datetime import datetime

class Person(model.Model)
     unique_id = models.CharField(max_length=25)
     age = models.IntegerField()
     last_name = models.CharField(max_length=25)
     birth_date = models.DateField()
     city_of_birth = models.CharField(max_length=25)
 
     @property
     def get_unique_id(self):
         a = self.last_name[:2].upper()     #First 2 letters of last name
         b = self.birth_date.strftime('%d')     #Day of the month as string
         c = self.city_of_birth[:2].upper()     #First 2 letters of city
         return a + b + c 
 
     @property
     def get_age(self):
         return relativedelta(self.birth_date.days, datetime.date.now()).years
     

     def save(self, *args, **kwargs):
          self.unique_id = self.get_unique_id
          self.age = self.get_age
          super(Person, self).save(*args, **kwargs)

UPDATE: Previously the self.get_unique_id and self.get_age were being called with '()' which is not required for class properties.