Ron Raney
Ron Raney

Reputation: 380

Using save() in Django model produces TypeError

*****Working with Django 1.11.x and Python 3.6*****

I'm trying to learn how to use the save() method in a Django model (models.py). There are two fields here that I want to become custom, 'calculated' fields (unique_id and age).

First I initiate the field variables, then define methods/properties based on existing fields, then I try to save the method results into the fields that I created.

from django.db import models
from dateutil.relativedelta import relativedelta
from datetime import datetime

class Person(models.Model):
    unique_id = models.CharField(max_length=6, primary_key=True)
    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, **kwarg):
        self.unique_id = self.get_unique_id()
        self.age = self.get_age()
        super(Person, self).save(*args, **kwarg)

    def __str__(self):
        return self.unique_id

First, I create 5 fields. 2 of them are placeholders: unique_id and age. Then I define two @property methods and each return a different type of result. The "get_unique_id" function works, but I can't get the result stored in the database. The "get_age" function may or may not be working. I haven't been able to produce it yet.

My primary question is how to correctly use the save() function to override the initial field values (unique_id and age) with my 'calculated field' methods (get_unique_id and get_age).

My primary problem is that when I add a Person (using the Person ModelForm in /Admin), it produces a TypeError: 'str' object is not callable at the line "self.unique_id = self.get_unique_id()". I am currently using the Admin interface to test.

Eventually, I need to learn how to NOT display these 2 fields in the forms, since they will be calculated based on the other fields. I think I may have found documentation on Meta that might help. Also, I want the unique_id field to be the primary key, so I added this option to the initial field.

I have a secondary question (my apologies for being new to Django) about the *args, and **kwargs. Is it okay to leave them there? I'm really unsure about which arguments I need to use, if any, and whether or not it's necessary to include *args and **kwargs in the code.

NOTE: To anyone who helped me yesterday with this app, I really appreciate your help. I consider this to be different than my previous question, although I'm using much of the same code.

Upvotes: 1

Views: 730

Answers (1)

Moses Koledoye
Moses Koledoye

Reputation: 78556

Properties are not callable. When you access self.get_unique_id, Python makes the call to the underlying method decorated by @property behind the scenes, which in this case returns a string. You don't need to call it again, drop the parens:

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

OTOH, going by @DanielRoseman's comment, you don't need store the age in the database. Just calculate it when its needed. You could rename get_age as age and drop the age field, so age becomes a property.

Upvotes: 4

Related Questions