Ad

Django Customized User Model.... Where Does User.model(...) Come From?

- 1 answer

So, I am working my way through the jungle of the django documentation in order to create an online classifieds app. Since users are supposed to be anble to post their own classifieds online, I need to make use of the user authentication system. However, they need to be able to log in by email instead of username, which is why I need to come up with a customized user model.

Now, the example walk-through thats contained in the django documentation seems helpful:

https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#a-full-example

In order to understand django better, I am trying to go through the example line by line, and there is a point that I don't quite get:

class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
    """
    Creates and saves a User with the given email, date of
    birth and password.
    """
    if not email:
        raise ValueError('Users must have an email address')

    user = self.model(
        email=self.normalize_email(email),
        date_of_birth=date_of_birth,
    )

I don't get where the "self.model(...)" comes from. Is it a method? I cannot find it in BaseUserManager... where is it defined? I don't even quite know how to phrase the question precisely because I'm so puzzled about it... could anybody just enlighten me about what we are defining here and for what purpose? And from which django model does it originally come from?

Thanks for your support in advance!

Ad

Answer

A Django model takes a ModelBase as metaclass. Indeed, we see in the source code [GItHub]:

class Model(metaclass=ModelBase):
    # …

Now when you thus construct a model class (not an object), it will run the __new__ method of the metaclass. This method will check if the items in the class have a .contribute_to_class(..) method, as we can see in a (shorted) version of the __new__ method [GitHub] here:

    def __new__(cls, name, bases, attrs, **kwargs):
        # …
        for obj_name, obj in list(attrs.items()):
            if _has_contribute_to_class(obj):
                contributable_attrs[obj_name] = obj
            else:
                new_attrs[obj_name] = obj

        # …
        for obj_name, obj in contributable_attrs.items():
            new_class.add_to_class(obj_name, obj)
        # …
        return new_class

It will thus call the add_to_class with the name of the object, and the object (the field) on the method class.

This method is implemented as [GitHub]:

    def add_to_class(cls, name, value):
        if _has_contribute_to_class(value):
            value.contribute_to_class(cls, name)
        else:
            setattr(cls, name, value)

It will thus set the attribute to the class, and call the contribute_to_class on that object, with the model class as first parameter, and the name as second parameter.

If that object is a Manager object, the contribute_to_class method is implemented as [GitHub]:

    def contribute_to_class(self, model, name):
        self.name = self.name or name
        self.model = model

        setattr(model, name, ManagerDescriptor(self))

        model._meta.add_manager(self)

so it will set the model attribute of the manager to the class where you added the manager to. In case you thus add this to the User model, then self.model will refer to the User class. If you then call self.model(email='[email protected]', date_of_birth='1-1-2019'), then it will thus construct a User model object with '[email protected]' as email, and 1-1-2019 as date_of_birth.

Ad
source: stackoverflow.com
Ad