0
votes

I created a custom model extending AbstractUser in order to authenticate users by email instad of by username (but not wanting to drop the username, because it will also be used).

This was the first thing I made before running the first migration, everything worked correctly except in the Django admin, when I create a new user, I want these fields to be filled username email password

And the admin only ask me for the username and password. How could I add the email too? Here's my codes

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _


class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifier for 
    authentication instead of username.
    """
    def create_user(self, email, password, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError(_('The Email must be set'))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user
    
    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)


class User(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    objects = CustomUserManager()

    def __str__(self):
        return self.email

admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User as CustomUser


class CustomUserAdmin(UserAdmin):
    model = CustomUser
    list_display = ('email', 'is_staff', 'is_active',)
    list_filter = ('eamil', 'is_staff', 'is_active',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Permissions', {'fields': ('is_staff', 'is_active')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)

admin.site.register(CustomUser, UserAdmin)

What I have to do? It seems that everything is correct, but the required email field is missing. Notice that this only happens when I use the Add User feature in the admin. When I create a superuser or when I login to the admin, everything is working as expected.

UPDATE: Here's the content of my forms.py

from django.contrib.auth.forms import UserCreationForm
# the user model was customized it should be invoked
from django.contrib.auth import get_user_model
from django.contrib.auth import forms as auth_forms
from .models import User as CustomUser


class UserChangeForm(auth_forms.UserChangeForm):
    class Meta(auth_forms.UserChangeForm.Meta):
        model = CustomUser

class UserCreationForm(auth_forms.UserCreationForm):
    class Meta(auth_forms.UserCreationForm.Meta):
        model = CustomUser
        fields = ("username", "email")


class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        # the user model was customized it should be invoked
        model = get_user_model()
        fields = UserCreationForm.Meta.fields + ("email",)

And here the content (updated) of my admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User as CustomUser
from .forms import UserChangeForm, UserCreationForm

class CustomUserAdmin(UserAdmin):
    model = CustomUser
    list_display = ('email', 'username', 'is_staff', 'is_active',)
    list_filter = ('eamil', 'username', 'is_staff', 'is_active',)
    form = UserChangeForm
    add_form = UserCreationForm
    
    fieldsets = (
        (None, {'fields': ('email', 'username', 'password')}),
        ('Permissions', {'fields': ('is_staff', 'is_active')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'username', 'password1', 'password2', 'is_staff', 'is_active')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)

admin.site.register(CustomUser, UserAdmin)

But I still have the same problem, I still can't add an email field in /admin/users/user/add/

2

2 Answers

0
votes

The UserAdmin class has the attributes form and add_form which point to form classes to be used to edit and create the user respectively. You need to override these form classes and also set these attributes yourself as these two forms are created for the default user model.

Firstly define these custom forms:

from django.contrib.auth import forms as auth_forms
from .models import User as CustomUser

class UserChangeForm(auth_forms.UserChangeForm):
    class Meta(auth_forms.UserChangeForm.Meta):
        model = CustomUser


class UserCreationForm(auth_forms.UserCreationForm):
    class Meta(auth_forms.UserCreationForm.Meta):
        model = CustomUser
        fields = ("username", "email")

Next set these in your CustomUserAdmin:

from .forms import UserChangeForm, UserCreationForm


class CustomUserAdmin(UserAdmin):
    # Your other attributes here
    form = UserChangeForm
    add_form = UserCreationForm
0
votes

In your code, I see admin.site.register(CustomUser, UserAdmin) but you have to register through your CustomUserAdmin. Change your admin.py code to admin.site.register(CustomUser, CustomUserAdmin)