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?

Asked By: Turgut

||

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.

Answered By: COSHW
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.