1

How am I able to print the email as self.email if it's a class attribute? Wouldn't we have to pass it to the parent class init via the super function or create our own init to do that?

class Account(AbstractBaseUser):
    email = models.EmailField(verbose_name="email", max_length=100, unique=True)

    # Required fields
    date_joined = models.DateTimeField(verbose_name="date_joined", auto_now_add=True)
    last_login = models.DateTimeField(verbose_name="last_login", auto_now=True)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email 

1 Answers1

1

The short answer is that Django replaces our declared fields with a descriptor (used to manage attributes of a class) that performs the task of getting / setting correct values.

The long answer is that the Model class has a custom metaclass (classes that control the creation of a class) named ModelBase. ModelBase calls add_to_class on all objects that have a contribute_to_class method, model fields (and many other classes in Django) do have this method and hence add_to_class is called on them which in turn calls contribute_to_class which has the following lines [GitHub] that makes the replacement with the descriptor:

if self.column:
    setattr(cls, self.attname, self.descriptor_class(self))

Here is a link to the source code [GitHub] for the descriptor used for most of the model fields.

Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
  • I don't see a model class defined though? – Denzel Hooke Aug 02 '21 at 17:12
  • @DenzelHooke `AbstractBaseUser` inherits from `Model` of course, see the [source code](https://github.com/django/django/blob/4b6208ffdd93d5d379d247ec094d567ef208effd/django/contrib/auth/base_user.py#L47) – Abdul Aziz Barkat Aug 02 '21 at 17:13
  • So if correct me if I'm wrong, still learning, We pull in all of the code from the model class through inheritance, the metaclass is now 'attached' to our class since we inherited from model base, the metaclass looks through each field object being instantiated within my Account class and calls add_to_class on each object if they have the contribute_to_class method. contribute_to_class is called on each object and setattr replaces the class attribute with the descriptor instance? – Denzel Hooke Aug 02 '21 at 17:37
  • 1
    @DenzelHooke basically yes, with the minor clarification that the metaclass in fact is the one that _creates_ our class, so all of that happens when the class itself is being created, this is possible because well a class is also an object (of type `type` and a metaclass inherits from `type`) in python. – Abdul Aziz Barkat Aug 02 '21 at 17:49
  • Yeah I just learned the basics of type what it actually does in regard to classes. Thanks a lot, it's all starting to click very well now – Denzel Hooke Aug 02 '21 at 18:49