Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django error admin.E033: username is not an attribute of users.CustomUser. Why is my custom user admin not working?

Tags:

python

django

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!

like image 495
thehumaneraser Avatar asked Aug 01 '19 17:08

thehumaneraser


People also ask

Why is my Django admin not working?

'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.


2 Answers

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',)
like image 102
Daniel Roseman Avatar answered Sep 28 '22 03:09

Daniel Roseman


class CustomUserAdmin(BaseUserAdmin):
        ordering = ("any field in  list_display")
like image 25
kamula Avatar answered Sep 28 '22 04:09

kamula