Search code examples
djangodjango-modelsdjango-authenticationdjango-users

Creating Custom User Class in Django


I've read and re-read the other questions regarding this issue and I'm still unable to create a custom Django User model. I keep getting the error: Manager isn't available; 'auth.User' has been swapped for 'Users.User'.

If not clear by the error, I've created a Users app in which the models.py file defines a custom user class User.

Here's are the relevant files from my project, UserTest:

  1. Registering the app, specifying the custom user model:

UserTest> UserTest> settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'Users'
]

AUTH_USER_MODEL = 'Users.User'
  1. Extending the default Django User class via my User model and connecting it to a Profile model (not strictly relevant to the issue but a fundamental aspect of the approach.)

UserTest > Users > models.py

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    email = models.CharField(max_length=128, blank=False, unique=True, verbose_name='Email', name='email')


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    ...
  1. Registering the new User model in my Users App's admin.py (not sure if this is required for testing quick front-end functionality?)

UserTest > Users > admin.py

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

admin.site.register(User, UserAdmin)

I've created some basic forms and a view to allow front-end submission of relevant user and profile data. Given the error, I didn't feel they were relevant but I can include them if someone feels they are. Again, the error is as follows:

Manager isn't available; 'auth.User' has been swapped for 'Users.User'

I had initially created a custom User model using the AbstractBaseUser class, which the documentation describes states a custom Manager class must also be created. I had the same error using that approach and didn't read that the same customization was needed when using this approach, relying on AbstractUser

In my UserTest > Users > forms.py file, I've tried accessing my Users.models.User model in both of the following ways:

  1. By directly importing it as such:

from .models import User

  1. By using Django's get_user_model() function as such:

from django.contrib.auth import get_user_model User = get_user_model()

The second approach seems to have solved this issue for many others, such as the question HERE but doesn't help me (unless I'm missing something).

The full traceback of the error is here:

Internal Server Error: /test-form/
Traceback (most recent call last):
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\core\handlers\base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\core\handlers\base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\...\Desktop\UserTest\Users\views.py", line 19, in NewUserRegistration
    print("USERFORM:", user_form, type(user_form), dir(user_form))
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\utils\html.py", line 397, in <lambda>
    klass.__str__ = lambda self: mark_safe(klass_str(self))
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 142, in __str__
    return self.as_table()
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 284, in as_table
    errors_on_separate_row=False,
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 202, in _html_output
    top_errors = self.non_field_errors()  # Errors that should be displayed above all fields.
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 313, in non_field_errors
    return self.errors.get(NON_FIELD_ERRORS, self.error_class(error_class='nonfield'))
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 180, in errors
    self.full_clean()
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\forms.py", line 383, in full_clean
    self._post_clean()
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\contrib\auth\forms.py", line 107, in _post_clean
    super()._post_clean()
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\forms\models.py", line 403, in _post_clean
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\db\models\base.py", line 1137, in full_clean
    self.clean()
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\contrib\auth\models.py", line 338, in clean
    self.email = self.__class__.objects.normalize_email(self.email)
  File "C:\Users\...\Desktop\UserTest\venv\lib\site-packages\django\db\models\manager.py", line 188, in __get__
    cls._meta.swapped,
AttributeError: Manager isn't available; 'auth.User' has been swapped for 'Users.User'
[26/Oct/2018 09:47:02] "POST /test-form/ HTTP/1.1" 500 118088

I appreciate any help. Usually, if I'm able to type an entire post on SO without having an "aha" moment I know I'm pretty screwed.

UPDATE 1: Adding forms.py and views.py

In my UserTest > Users app, the views.py and forms.py are as follows:

UserTest > Users > forms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError

from django.contrib.auth import get_user_model
User = get_user_model()


class NewUser(UserCreationForm):
    email = forms.EmailField(max_length=96)
    password = forms.PasswordInput()
    username = forms.CharField(max_length=18)

    def clean_email(self):
        email = self.cleaned_data['email'].lower()
        r = User.objects.filter(email=email)
        if r.count():
            raise ValidationError("Email already exists")
        return email

    def save(self, commit=True):

        user = User.objects.create_user(
            self.cleaned_data['email'],
            self.cleaned_data['password'],
            self.cleaned_data['username']
        )
        return user

NOTE: Here I have tried both the get_user_model() approach as well as importing my custom User class from Users.models and neither resolve the error.

UserTest > Users > views.py

from django.shortcuts import render
from django.contrib import messages
from .forms import NewUser


def NewUserRegistration(request):

    if request.method == 'POST':
        user_form = NewUser(request.POST)

        if user_form.is_valid():
            user_form.save()
            messages.success(request, 'Account Created Successfully!')
    else:
        user_form = NewUser()

    return render(request, 'Users/test-form.html', {'user_form': user_form})

Solution

  • As the documentation explains, forms such as UserCreationForm" are tied to User and need to be rewritten or extended to work with a custom user model".

    So, as shown there, you need to override the inner Meta class to point to your model:

    class NewUser(UserCreationForm):
        ...
        class Meta(UserCreationForm.Meta):
            model = get_user_model()