Django AttributeError: type object 'Admin' has no attribute '_meta' error when creating custom user
Question:
I’m learning django and I want to have my custom user and admin object but I’m encountering an error when running server after applying my changes. I’m not sure if this is the right approach to do this so do enlighten me if I’m following the wrong approach.
So I have created an app called ‘core’ where I want to do authentication stuff so that’s where I’ve put my custom user models.
Here are the files I’ve written for that task so far:
models.py:
from django.db import models
from store.models import CartItem
from django.contrib.auth.models import AbstractBaseUser, UserManager
from enum import Enum
from django_countries.fields import CountryField
from django.contrib import admin
ADDRESS_CHOICES = (
('B', 'Billing'),
('S', 'Shipping'),
)
class AuthLevel(Enum):
NONE = 0
Customer = 1
Admin = 2
Programmer = 3
class Address(models.Model):
street_address = models.CharField(max_length=100)
apartment_address = models.CharField(max_length=100)
country = CountryField(multiple=False)
zip = models.CharField(max_length=100)
address_type = models.CharField(max_length=1, choices=ADDRESS_CHOICES)
default = models.BooleanField(default=False)
def __str__(self):
return self.street_address.name + " " + self.apartment_address.name
class Meta:
verbose_name_plural = 'Addresses'
class BaseUser(AbstractBaseUser):
name = models.CharField(max_length=100, unique=True)
auth_level = AuthLevel.NONE
class Customer(AbstractBaseUser):
username = models.CharField(max_length=100, unique=True)
email = models.EmailField()
address = models.OneToOneField(Address, on_delete=models.CASCADE)
stripe_customer_id = models.CharField(max_length=50, blank=True, null=True)
one_click_purchasing = models.BooleanField(default=False)
cart = models.ManyToManyField(CartItem) #on_delete=models.CASCADE)
auth_level = AuthLevel.Customer
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
objects = UserManager()
def __str__(self):
return self.user.username
class Worker(models.Model):
auth_level = AuthLevel.Worker
class Admin(admin.ModelAdmin):
auth_level = AuthLevel.Admin
list_display = ('username', 'email', 'last_login')
list_filter = ('is_superuser',)
search_fields = ('username', 'email')
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(pk=request.user.pk)
class Programmer(Owner):
auth_level = AuthLevel.Programmer
def __str__(self):
return self.user.username + ": PROGRAMMER"
backends.py
from django.shortcuts import get_object_or_404
from .models import Customer
import logging
from django.contrib.auth.backends import BaseBackend
class AuthCustomer(BaseBackend):
def authenticate(self, request, name=None, password=None):
try:
user = get_object_or_404(Customer, name=name)
if user.check_password(password):
return user
else:
return None
except Customer.DoesNotExist:
logging.getLogger("error_logger").error("user with login %s does not exists ")
return None
except Exception as e:
logging.getLogger("error_logger").error(repr(e))
return None
def get_user(self, user_id):
try:
user = Customer.objects.get(id=user_id)
if user.is_active:
return user
return None
except Customer.DoesNotExist:
logging.getLogger("error_logger").error("user with %(user_id)d not found")
return None
admin.py
from django.contrib import admin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from core.models import Admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group
from django import forms
class UserCreationForm(forms.ModelForm):
# A form for creating new users. Includes all the required
# fields, plus a repeated password.
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = Admin
fields = ('name',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
# A form for updating users. Includes all the fields on
# the user, but replaces the password field with admins
# password hash display field.
password = ReadOnlyPasswordHashField()
class Meta:
model = Admin
fields = ('name', 'address', 'desc', 'is_staff')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class AdminModelAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('name', 'is_staff')
list_filter = ('name',)
fieldsets = (
(None, {'fields': ('name', 'password', 'desc', 'address')}),
('Permissions', {'fields': ('is_staff', 'auth_level')}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('name', 'password1', 'password2')}
),
)
search_fields = ('name',)
ordering = ('name',)
filter_horizontal = ()
admin.site.register(Admin, AdminModelAdmin)
admin.site.unregister(Group)
And here are the changes I’ve done in my settings.py:
settings.py
...
INSTALLED_APPS = [
'core',
...
]
...
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = "core.Customer"
My goal is to have customer as the user form people register with and have Admin as the super user model used by djangos admin panel.
When I run python manage.py runserver
I get this error:
PS C:UsersAsusDesktopProjectsEcommerce_tutorial> python manage.py runserver
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libthreading.py", line 1038, in _bootstrap_inner
self.run()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libthreading.py", line 975, in run
self._target(*self._args, **self._kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocoremanagementcommandsrunserver.py", line 125, in inner_run
autoreload.raise_last_exception()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 87, in raise_last_exception
raise _exception[1]
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocoremanagement__init__.py", line 398, in execute
autoreload.check_errors(django.setup)()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjango__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoappsregistry.py", line 124, in populate
app_config.ready()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocontribadminapps.py", line 27, in ready
AttributeError: type object 'Admin' has no attribute '_meta'
I’ve searched some related questions on stackoverflow and tried doing them to no avail. How can I fix this? Is there a better way to implement this?
Answers:
I think that the problem is in your admin.py.
admin.site.register takes a BaseModel instance and ModelAdmin instance.
Try to change
admin.site.register(Admin, AdminModelAdmin)
to
admin.site.register(Customer, AdminModelAdmin)
Change Customer to needed user model.
Or maybe you wanted to make specific model for admin users, but used wrong parent class, since admin.ModelAdmin is not a model. So if you want to have a Admin as a model, change parent to AbstractBaseUser, but this is generally a better idea to have a single AbstractBaseUser.
I’m learning django and I want to have my custom user and admin object but I’m encountering an error when running server after applying my changes. I’m not sure if this is the right approach to do this so do enlighten me if I’m following the wrong approach.
So I have created an app called ‘core’ where I want to do authentication stuff so that’s where I’ve put my custom user models.
Here are the files I’ve written for that task so far:
models.py:
from django.db import models
from store.models import CartItem
from django.contrib.auth.models import AbstractBaseUser, UserManager
from enum import Enum
from django_countries.fields import CountryField
from django.contrib import admin
ADDRESS_CHOICES = (
('B', 'Billing'),
('S', 'Shipping'),
)
class AuthLevel(Enum):
NONE = 0
Customer = 1
Admin = 2
Programmer = 3
class Address(models.Model):
street_address = models.CharField(max_length=100)
apartment_address = models.CharField(max_length=100)
country = CountryField(multiple=False)
zip = models.CharField(max_length=100)
address_type = models.CharField(max_length=1, choices=ADDRESS_CHOICES)
default = models.BooleanField(default=False)
def __str__(self):
return self.street_address.name + " " + self.apartment_address.name
class Meta:
verbose_name_plural = 'Addresses'
class BaseUser(AbstractBaseUser):
name = models.CharField(max_length=100, unique=True)
auth_level = AuthLevel.NONE
class Customer(AbstractBaseUser):
username = models.CharField(max_length=100, unique=True)
email = models.EmailField()
address = models.OneToOneField(Address, on_delete=models.CASCADE)
stripe_customer_id = models.CharField(max_length=50, blank=True, null=True)
one_click_purchasing = models.BooleanField(default=False)
cart = models.ManyToManyField(CartItem) #on_delete=models.CASCADE)
auth_level = AuthLevel.Customer
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
objects = UserManager()
def __str__(self):
return self.user.username
class Worker(models.Model):
auth_level = AuthLevel.Worker
class Admin(admin.ModelAdmin):
auth_level = AuthLevel.Admin
list_display = ('username', 'email', 'last_login')
list_filter = ('is_superuser',)
search_fields = ('username', 'email')
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(pk=request.user.pk)
class Programmer(Owner):
auth_level = AuthLevel.Programmer
def __str__(self):
return self.user.username + ": PROGRAMMER"
backends.py
from django.shortcuts import get_object_or_404
from .models import Customer
import logging
from django.contrib.auth.backends import BaseBackend
class AuthCustomer(BaseBackend):
def authenticate(self, request, name=None, password=None):
try:
user = get_object_or_404(Customer, name=name)
if user.check_password(password):
return user
else:
return None
except Customer.DoesNotExist:
logging.getLogger("error_logger").error("user with login %s does not exists ")
return None
except Exception as e:
logging.getLogger("error_logger").error(repr(e))
return None
def get_user(self, user_id):
try:
user = Customer.objects.get(id=user_id)
if user.is_active:
return user
return None
except Customer.DoesNotExist:
logging.getLogger("error_logger").error("user with %(user_id)d not found")
return None
admin.py
from django.contrib import admin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from core.models import Admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group
from django import forms
class UserCreationForm(forms.ModelForm):
# A form for creating new users. Includes all the required
# fields, plus a repeated password.
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = Admin
fields = ('name',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
# A form for updating users. Includes all the fields on
# the user, but replaces the password field with admins
# password hash display field.
password = ReadOnlyPasswordHashField()
class Meta:
model = Admin
fields = ('name', 'address', 'desc', 'is_staff')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class AdminModelAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('name', 'is_staff')
list_filter = ('name',)
fieldsets = (
(None, {'fields': ('name', 'password', 'desc', 'address')}),
('Permissions', {'fields': ('is_staff', 'auth_level')}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('name', 'password1', 'password2')}
),
)
search_fields = ('name',)
ordering = ('name',)
filter_horizontal = ()
admin.site.register(Admin, AdminModelAdmin)
admin.site.unregister(Group)
And here are the changes I’ve done in my settings.py:
settings.py
...
INSTALLED_APPS = [
'core',
...
]
...
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = "core.Customer"
My goal is to have customer as the user form people register with and have Admin as the super user model used by djangos admin panel.
When I run python manage.py runserver
I get this error:
PS C:UsersAsusDesktopProjectsEcommerce_tutorial> python manage.py runserver
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libthreading.py", line 1038, in _bootstrap_inner
self.run()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libthreading.py", line 975, in run
self._target(*self._args, **self._kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocoremanagementcommandsrunserver.py", line 125, in inner_run
autoreload.raise_last_exception()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 87, in raise_last_exception
raise _exception[1]
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocoremanagement__init__.py", line 398, in execute
autoreload.check_errors(django.setup)()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoutilsautoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjango__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangoappsregistry.py", line 124, in populate
app_config.ready()
File "C:UsersAsusAppDataLocalProgramsPythonPython311Libsite-packagesdjangocontribadminapps.py", line 27, in ready
AttributeError: type object 'Admin' has no attribute '_meta'
I’ve searched some related questions on stackoverflow and tried doing them to no avail. How can I fix this? Is there a better way to implement this?
I think that the problem is in your admin.py.
admin.site.register takes a BaseModel instance and ModelAdmin instance.
Try to change
admin.site.register(Admin, AdminModelAdmin)
to
admin.site.register(Customer, AdminModelAdmin)
Change Customer to needed user model.
Or maybe you wanted to make specific model for admin users, but used wrong parent class, since admin.ModelAdmin is not a model. So if you want to have a Admin as a model, change parent to AbstractBaseUser, but this is generally a better idea to have a single AbstractBaseUser.