简体   繁体   中英

Django custom user field clashes with AbstractBaseUser

I am building a Django project from an existing database. The database is being used by other systems, so I cannot change its schema. This is my current custom User model:

class Users(AbstractBaseUser):
    id_user = models.IntegerField(primary_key=True)
    role = models.IntegerField()
    username = models.CharField(max_length=50, unique=True)
    last_login_date = models.DateTimeField()

AbstractBaseUser needs a column named last_login , while current database table has last_login_date column which serves like AbstractBaseUser.last_login . Now I need to use that column in Users.last_login :

    ...
    last_login = models.DateTimeField(_('last login'), default=timezone.now, column_name='last_login_date')
    ...

However Django would throw django.core.exceptions.FieldError: Local field 'last_login' in class 'Users' clashes with field of similar name from base class 'AbstractBaseUser' since Django does not allow overriding parent's fields.

How to set the fields?

Although there is an answer that already satisfied the question I want to contribute with another way of achieving the same task in a more robust way.

As you already know, Django AbstractBaseUser is the base class that should be used to substitute Django User Class. Such a class inherits from models.Model with is the one that actually creates the model.

This class takes advantage of the metaclass of the python data model to alter the creation process.

And that's exactly what we should do. As you can read on Python Data Model you can use metaclass special attribute to alter the creation process as you could see. In your case you could have done the following:

def myoverridenmeta(name, bases, adict):
    newClass = type(name, bases, adict)
    for field in newClass._meta.fields:
        if field.attname == 'last_login':
            field.column = 'last_login_date'
            field.db_column = 'last_login_date'
    return newClass

class Users(AbstractBaseUser):
    id_user = models.IntegerField(primary_key=True)
    role = models.IntegerField()
    username = models.CharField(max_length=50, unique=True)

    __metaclass__ = myoverridenmeta

I can't figure out a good way to do this, so I'll give you two rather unsatisfying (but workable) solutions hacks:

  1. Rather than inheriting from AbstractBaseUser, take advantage of Django's open-source-ness and copy their AbstractBaseUser code (it's located at <...>lib/python3.4/site-packages/django/contrib/auth/models.py) and use a direct implementation of it with column_name='last_login_date' in the last_login field. (the AbstractBaseUser class is also here (version 1.7))

  2. Edit <...>lib/python3.4/site-packages/django/contrib/auth/models.py directly (resulting in non-portable code that will not work on another django installation without hacking it too)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM