I am creating a custom user model in Django. I have defined a custom user model (users.CustomUser) which subclasses AbstractBaseUser. I have created a custom user manager (users.CustomUserManager) which subclasses BaseUserManager and works properly. I have also created a custom user admin which subclasses UserAdmin since my CustomUser model does not have a username field (it uses 'email' instead).
As far as I can tell, I have coded everything properly, but when I run 'python manage.py makemigrations' I get an error message:
<class 'users.admin.CustomUserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'users.CustomUser'.
I'm stuck here.
I have already tried the following: (1) Defined username field to be email in my custom user model class (2) Tried setting username to None in my custom user model class and custom user admin (3) Created custom user registration and change forms and registered them with my custom user admin
# models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from phonenumber_field.modelfields import PhoneNumberField
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
username = None
firstname = models.CharField(max_length = 60)
lastname = models.CharField(max_length = 60)
email = models.EmailField(max_length = 240, unique=True)
phone = PhoneNumberField(null=True, blank=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, null=True, blank=True)
password = models.CharField(max_length = 240)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['firstname', 'lastname', 'company', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# managers.py
from django.contrib.auth.base_user import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, firstname, lastname, company, password, **extra_fields):
email = self.normalize_email(email)
user = self.model(
email=email,
firstname=firstname,
lastname=lastname,
company=company,
**extra_fields
)
user.set_password(password)
user.save()
return user
#forms.py
from django.contrib.auth.models import User
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser, Company
from phonenumber_field.modelfields import PhoneNumberField
from django.core.exceptions import ValidationError
class CustomUserRegistrationForm(forms.ModelForm):
firstname = forms.CharField(label = 'First Name*', max_length = 120)
lastname = forms.CharField(label = 'Last Name*', max_length = 120)
email = forms.EmailField(label = 'Email*')
phone = PhoneNumberField()
company = forms.ModelChoiceField(queryset = Company.objects.all(), label = 'Company*', required = True)
password = forms.CharField(label = 'Password*', min_length = 5, max_length = 50, widget = forms.PasswordInput)
password2 = forms.CharField(label = 'Confirm Password*', min_length = 5, max_length = 50, widget = forms.PasswordInput)
class Meta:
model = CustomUser
fields = ('firstname', 'lastname', 'company', 'email', 'phone', 'password')
def clean_email(self):
email = self.cleaned_data['email'].lower()
user_list = CustomUser.objects.filter(email=email)
if user_list.count():
raise ValidationError('There is already an account associated with that email.')
return email
def clean_password2(self):
password1 = self.cleaned_data['password']
password2 = self.cleaned_data['password2']
if (password1 and password2) and (password1 != password2):
raise ValidationError('Passwords do not match.')
return password2
def save(self, commit=True):
context = {
'firstname':self.cleaned_data['firstname'],
'lastname':self.cleaned_data['lastname'],
'email':self.cleaned_data['email'],
'phone':self.cleaned_data['phone'],
'password':self.cleaned_data['password'],
'admin':'',
'company':self.cleaned_data['company'],
}
custom_user = CustomUser.objects.create_user(
context['email'],
context['firstname'],
context['lastname'],
context['company'],
context['password']
)
return custom_user
class CustomUserChangeForm(UserChangeForm):
firstname = forms.CharField(label = 'First Name', max_length = 120)
lastname = forms.CharField(label = 'Last Name', max_length = 120)
email = forms.EmailField(label = 'New Email')
phone = PhoneNumberField()
old_password = forms.CharField(label = 'Current Password', min_length = 5, max_length = 50, widget = forms.PasswordInput)
new_password = forms.CharField(label = 'New Password', min_length = 5, max_length = 50, widget = forms.PasswordInput)
new_password2 = forms.CharField(label = 'Confirm New Password', min_length = 5, max_length = 50, widget = forms.PasswordInput)
class Meta:
model = CustomUser
exclude = ['company',]
def clean_new_password(self):
new_password = self.cleaned_data['new_password']
new_password2 = self.cleaned_data['new_password2']
if (new_password and new_password2) and (new_password != new_password2):
raise ValidationError('Passwords do not match.')
if not (new_password and new_password2):
raise ValidationError('Please enter new password twice.')
return new_password
def clean_email(self):
email = self.cleaned_data['email']
email_list = CustomUser.objects.filter(email=email)
if email_list.count():
raise ValidationError('There is already an account associated with that email.')
return email
# admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import CustomUser
from .forms import CustomUserRegistrationForm, CustomUserChangeForm
class CustomUserAdmin(BaseUserAdmin):
add_form = CustomUserRegistrationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ('firstname', 'lastname', 'email', 'company')
list_filter = ('company',)
fieldsets = (
(None, {'fields': ('email', 'old_password', 'new_password', 'new_password2')}),
('Personal Information', {'fields': ('firstname', 'lastname', 'phone')}),
)
add_fieldsets = (
(None, {'fields': ('email', 'password', 'password2')}),
('Personal Information', {'fields': ('firstname', 'lastname', 'phone')}),
('Company Information', {'fields': ('company',)}),
)
admin.site.register(CustomUser, CustomUserAdmin)
I expect to be able to migrate the database properly and use my custom user model on my site (i.e. allow users to sign up and create profiles using the custom fields I have outlined). Instead, when trying to run migrations in the Command Prompt, I get the error shown above.
Any help is appreciated! Thanks!
'django-admin' is not recognized as an internal or external command, operable program or batch file. To fix this, first close the terminal window and relaunch it with administrator privileges. Once you launch the elevated terminal window change directory to where you wish to start your Django project.
Like the error says, the admin class for User by default orders by username. Since you don't have a username, you should override that:
class CustomUserAdmin(BaseUserAdmin):
...
ordering = ('email',)
class CustomUserAdmin(BaseUserAdmin):
ordering = ("any field in list_display")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With