10

Trying to create my custom user model, this is the code I have:

models.py

class CustomUser(AbstractUser):
    USERNAME_FIELD = 'email'

CustomUser._meta.get_field_by_name('email')[0]._unique=True

settings.py

AUTH_USER_MODEL = 'myapp.CustomUser'

When doing the manage.py syncdb, the following error occurs:

CommandError: One or more models did not validate:
myapp.customuser: The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.

Can anyone shed some light? Is there a better way of customizing the user model in Django 1.6 without rewriting the entire class extending AbstractBaseUser?

Btw, if I delete USERNAME_FIELD = 'email' from my code and change the core django auth/models.py >> AbstractUser definition, it works. I just can't seem to be able to override the USERNAME_FIELD...

Thanks!

Rok
  • 1,482
  • 3
  • 18
  • 37

3 Answers3

12

As made clear by the error message, the reason for is that AbstractUser defines REQUIRED_FIELDS = ['email']. And you cannot set a field in the REQUIRED_FIELDS to be your USERNAME_FIELD. Further detail here.

So if you want email as your primary field the way forward is to extend and redefine the field email on AbstractBaseUser rather than on AbstractUser.

Also this question/answer pair might be relevant to you in case I did not fully grasp your requirements.

Community
  • 1
  • 1
Joseph Victor Zammit
  • 14,760
  • 10
  • 76
  • 102
7

Josvic, your solution partially resolved my issue. What I had to do in the end was this:

class CustomUser(AbstractUser):

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def get_username(self):
        return self.email

CustomUser._meta.get_field_by_name('email')[0]._unique=True

This resolved the initial issue that REQUIRED_FIELDS cannot contain the USERNAME_FIELD and as I do not want to redefine the entire User and extend the AbstractBaseUser, I had to include the 'username' in the REQUIRED_FIELD as otherwise the syncdb fails for a different reason (expecting the username for the sake of saving it into the db):

self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
TypeError: create_superuser() takes exactly 4 arguments (3 given)

Consequently, when doing the syncdb for the first time and having to enter a superuser, the email must be entered twice, as an email as well as a username. It's awkward but it's the cleanest one that I can live with it.

Cheers!

Rok
  • 1,482
  • 3
  • 18
  • 37
  • Not sure about "cleanest" as you're setting a default in a parent class that I doubt is meant to be set in subclasses; I'm referring to `REQUIRED_FIELDS` property. Try to make this fact explicit in your codebase. I would simply inherit from a class higher in the hierarchy, even if that implies the implementation fields that `AbstractUser` already provides. – Joseph Victor Zammit Sep 17 '13 at 16:17
  • @rok in newer versions of Django the row `CustomUser._meta.get_field_by_name('email')[0]._unique=True` should be replaced with: `CustomUser._meta.get_field('email')._unique=True` – Sinisa Rudan Sep 19 '22 at 09:06
2

Josvic hit the nail on the head but I want to provide the solution I used. It's pretty simple.

  1. Remove 'email' or whatever you're using are USERNAME_FIELD from REQUIRE_FIELDS.

  2. In the UserManager that you made, simply write:

    if not email: raise ValueError('Users must have an email address')

This way, you'll manually enforce that an email is required.

This solution is the one that is used here: https://docs.djangoproject.com/en/1.7/topics/auth/customizing/#a-full-example

User
  • 23,729
  • 38
  • 124
  • 207