Using email as username field in Django 1.5 custom User model results in FieldError
Asked Answered
H

4

25

I want to use an email field as the username field for my custom user model. I have the following custom User model subclassing Django's AbstractUser model:

class CustomUser(AbstractUser):
    ....
    email = models.EmailField(max_length=255, unique=True)

    USERNAME_FIELD = 'email'

But when I run

python manage.py sql myapp

I get the following error:

FieldError: Local field 'email' in class 'CustomUser' clashes with field of similar name from base class 'AbstractUser'

The reason I include my own email field in the first place is to add the unique=True option to it. otherwise I get:

myapp.customuser: The USERNAME_FIELD must be unique. Add unique=True to the field parameters.

Now, in respect to this: https://docs.djangoproject.com/en/1.5/topics/db/models/#field-name-hiding-is-not-permitted
How can I achieve this? (other then naming the field "user_email" or something like that instead)

Heeley answered 24/3, 2013 at 10:9 Comment(0)
H
37

Ian, thank you very much for the clever response :)

However, I've already "patched" me a solution.

Since AbstractUser also have a username field which is totaly unnecessary for me
I decided to create my "own" AbstractUser.

By subclassing AbstractBaseUser and PermissionsMixin I retain most of the User model built-in methods without adding any code.

I also took advantage of that opportunity to create a custom Manager to eliminate the use in username field all together:

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager

class CustomUser(AbstractBaseUser, PermissionsMixin):
     ....
     email = models.EmailField(max_length=255, unique=True)
     first_name = ...
     last_name = ...
     is_active = ...
     is_staff = ...
     ....

     objects = CustomUserManager()

     USERNAME_FIELD = 'email'


class CustomUserManager(BaseUserManager):
     def create_user(self, email, password=None, **extra_fields):
          .....

     def create_superuser(self, email, password, **extra_fields):
          .....

This solution does result in repetition of some of Django's built-in code (mainly model fields that already exist in AbstractUser such as 'first_name', 'last_name' etc.) but also in a cleaner User object and database table.

It is a real shame that the flexibily introduced in 1.5 with USERNAME_FIELD can not be used to actualy create a flexible User model under all existing constrains.

EDIT: There is a comprehensive worked example available in the official docs: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#a-full-example

Heeley answered 25/3, 2013 at 0:30 Comment(5)
What does your create_superuser method contain?Nicholson
It creates a user object with no username field (with email instead) using create_user: u = self.create_user(email, password, **extra_fields), sets the superuser "special" fields (is_staff, is_superuser, ...) and then saves that user.Heeley
is implementation of CustomUserMananger a must have or can I skip it and deafault django's staff will be aplied?Clutter
@andi From what I saw back then, the username argument was mandatory for create_superuser under Django's UserManager. Because I removed the username field from my user model, this gave me an error when I was trying to create a superuser through the Linux shell using Django's builtin "project creation" procces (which uses Django's createsuperuser script, which in turn uses create_superuser method from your CustomUser model manager). Overrinding and defining create_superuser without username solved the problem.Heeley
As I couldn't insert the link in my previous comment (the comment was "too long"), here's Django's own UserManagerHeeley
B
12

If your real target is unique "email" values, and ignoring "username" values, then you may:

  • Fill "username" with e.g. sha256(user.email).hexdigest()[:30]
  • Add uniqueness this way:

    class User(AbstractUser):
        class Meta:
            unique_together = ('email', )
    

This results in:

CREATE TABLE "myapp_user" (
    ...
    "email" varchar(75) NOT NULL,
    UNIQUE ("email")
)

works just as expected, and is pretty simple.

Biliary answered 3/7, 2013 at 17:40 Comment(5)
This was the best option that I have seen when dealing with this matter. Thanks a lot!Selfmade
Where do you put the code to fill username?Esquiline
@Esquiline there are many ways, e.g. docs.djangoproject.com/en/dev/topics/auth/default/…Biliary
@DenisRyzhkov That is good when I create user in my code, but it's the backend, e.g. allauth that creates users. So, I need to somehow instrument it...Esquiline
@Esquiline try either django-allauth.readthedocs.io/en/latest/… or docs.djangoproject.com/en/4.0/topics/db/models/…Biliary
S
9

You can edit your CustomUser to change the email field attribute to unique=True.

Add this to the end of your custom user class like so:

class CustomUser(AbstractUser):
    ...
    USERNAME_FIELD = 'email'
    ...
CustomUser._meta.get_field_by_name('email')[0]._unique=True

Note that we're changing _unique and not unique because the latter is a simple @property.

This is a hack, and I would love to hear any "official" answers to resolve this.

Skat answered 24/3, 2013 at 20:50 Comment(1)
I had to use CustomUser._meta.get_field('email')._unique = TrueEsquiline
M
2

Use the example from the official site :

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

Here is an example of an admin-compliant custom user app. This user model uses an email address as the username, and has a required date of birth; it provides no permission checking, beyond a simple admin flag on the user account. This model would be compatible with all the built-in auth forms and views, except for the User creation forms. This example illustrates how most of the components work together, but is not intended to be copied directly into projects for production use.

Mcgriff answered 24/10, 2014 at 23:48 Comment(2)
This is actually the way to goPermissible
Is all this really necessary just to change the USERNAME_FIELD?Soniferous

© 2022 - 2024 — McMap. All rights reserved.