Search code examples
djangodjango-modelsdjango-inheritance

Django model is inheriting all super model Fields, except for IntegerField


I'm having a weird issue, I have a models' hierarchy, an abstract User class:

class User(AbstractBaseUser):
    user_name = models.CharField(max_length=32, unique=True)
    email = models.EmailField(max_length=255, unique=True, null=True)
    phone = PhoneNumberField()
    access_token = models.CharField(max_length=255, unique=True, null=True)
    notifications_token = models.CharField(max_length=255, unique=True, null=True)
    photo = models.ImageField(null=True)
    person_in_contact = models.CharField(max_length=32, null=True)
    active = models.BooleanField(default=False)
    confirmedEmail = models.BooleanField(default=False)
    confirmedPhone = models.BooleanField(default=False)
    completedProfile = models.BooleanField(default=False)
    visible = models.BooleanField(default=True)

    @property
    def is_active(self):
        return self.active

    # def __str__(self):
    #     return "Client : " + self.user_name + " Email:" + self.email

    def get_email(self):
        return self.email

    USERNAME_FIELD = 'user_name'

    REQUIRED_FIELDS = ['user_name', 'phone', 'password']

    class Meta:
        abstract = True

then a person class and a company (no issue with this one) class that inherit from this one:

class Person(User):
    GENDER = (('F', 'FEMALE'), ('M', 'MALE'))

    name = models.CharField(max_length=50, null=True)
    surname = models.CharField(max_length=50, null=True)
    adress = models.CharField(max_length=255, null=True)

    birth_date = models.DateField(null=True)
    gender = models.CharField(max_length=1, choices=GENDER, null=True)
    age = models.IntegerField(null=True)

    def age(self):
        today = date.today()
        return today.year - self.birth_date.year

    # def __str__(self):
    #     return super().__str__() + " Name : " + self.name

    class Meta:
        abstract = True

as you can see, the only field that's IntegerField() is the age field. now i have a Traveller and a Driver classes that inherit from the person class, the issue is the age field, doesn't show in the database, unless i override it in one of the classes, that's what i did, i override it in traveller so it appeared in the database, but didn't override it in the driver, so it didn't show. Traveller:

class Traveller(Person):
    photo = models.ImageField(null=True, upload_to='travellers/profile_pictures')
    age = models.IntegerField(null=True)

    class Meta:
        verbose_name_plural = 'Travellers'

Driver:

class Driver(Person):
    rating = models.DecimalField(default=0, decimal_places=1, max_digits=3)
    driving_license = models.CharField(max_length=50, null=True)
    insurance_number = models.CharField(max_length=50, null=True)
    company = models.ForeignKey(TransportCompany, on_delete=models.DO_NOTHING, null=True)
    photo = models.ImageField(null=True, upload_to='drivers/profile_pictures')

i need to know how i can fix this, or what's the issue, any help is appreciated.


Solution

  • The reason this happens is because your age field has the same name as the age function. As a result, the age = ... field is ignored by Python in favor of the age function, since that is the last time you defined the age variable.

    For the same reason the age field pops up in sublasses: you defined an age variable over there, and that takes precedence over the "inherited" age method.

    You should rename one of the two. For example with:

    class Person(User):
        GENDER = (('F', 'FEMALE'), ('M', 'MALE'))
    
        name = models.CharField(max_length=50, null=True)
        surname = models.CharField(max_length=50, null=True)
        adress = models.CharField(max_length=255, null=True)
    
        birth_date = models.DateField(null=True)
        gender = models.CharField(max_length=1, choices=GENDER, null=True)
        _age = models.IntegerField(null=True)
    
        def age(self):
            today = date.today()
            bod = self.birth_date
            before_dob = (today.month, today.day) < (bod.month, bod.day)
            return today.year - self.birth_date.year - before_dob
    
        class Meta:
            abstract = True

    Note that the calculation of the age was not completely accurate: if we are before the birthday of that year, you need to subtract one from the age.