Creating Custom User Class in Django - 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:
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'
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)
...
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:
By directly importing it as such:
from .models import User
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})

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()

When you use custom user model it needs to define its objects i.e its manager which contains its various methods like create_user or create_superuser.
You can use BaseUserManager to inherit in your custom manager and define it in your User model.
It could be like -
class UserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, date_of_birth, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractUser):
email = models.CharField(max_length=128, blank=False, unique=True, verbose_name='Email', name='email')
objects = UserManager()
This is quit simple example, you can use UserManager as per your requirements.
It is recommenced to refer documentation for more clear understanding - https://docs.djangoproject.com/en/2.1/topics/auth/customizing/

Related

I am unable to identify my error in writing unit test for Django rest_farmework

I am trying to write a unit test to check my view for password reset in Django rest_framework. I can't identify where is the error and why am I getting the error. I am a beginner in django rest_framework. I would appreciated your help.
my View.py
from user_authentication.models import User as Client
from rest_framework.response import Response
from rest_framework.views import APIView
class Password_Reset(APIView):
def post(self, request):
username = request.data.get('username')
if Client.objects.filter(username = username).exists():
new_password = request.data.get('new_password')
confirm_password = request.data.get('confirm_password')
if new_password == confirm_password:
user = Client.objects.filter(username = username).first()
user.set_password(new_password)
user.save()
return Response({"message": "your password has been changed"}, 201)
else:
return Response({"message": "This email address doesn't exist"}, 404)
my model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
created_at=models.DateTimeField(auto_now_add=True)
email = models.EmailField(verbose_name="Email", max_length=60, unique=True)
date_of_birth= models.DateField(verbose_name="Date of Birth", max_length=10)
def __str__(self):
return self.username
my urls.py
from django.urls import path
from user_authentication.views import Login, Logout, Register, Password_Reset
urlpatterns = [
path('login/', Login.as_view()),
path('logout/', Logout.as_view()),
path('register/', Register.as_view()),
path('login/password_reset', Password_Reset.as_view())
]
my Unit Test for password reset
from rest_framework.test import APITestCase
from user_authentication.models import User
from user_authentication.views import Password_Reset, Register, Login, Logout
class ViewTest(APITestCase):
def test_password_reset(self):
url = 'login/password_reset'
response = self.client.post(url, {'username': "developer", "new_password": "abc1234",
"confirm_password": "abc1234"})
user = User.objects.get(username = 'developer')
password= "abc1234"
self.assertTrue(user.check_password(password))
response.status_code
my Error
(venv) PS C:\Users\Lenovo\Documents\GitHub\E-commerce\garments> python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_password_reset (user_authentication.tests.ViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\Lenovo\Documents\GitHub\E-commerce\garments\user_authentication\tests.py", line 10, in test_password_reset
user = User.objects.get(username = 'developer')
File "C:\Users\Lenovo\Documents\GitHub\E-commerce\venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Lenovo\Documents\GitHub\E-commerce\venv\lib\site-packages\django\db\models\query.py", line 435, in get
raise self.model.DoesNotExist(
user_authentication.models.User.DoesNotExist: User matching query does not exist.
----------------------------------------------------------------------
Ran 1 test in 0.031s
FAILED (errors=1)
Destroying test database for alias 'default'...
You need to create a user first before :
class ViewTest(APITestCase):
def setUp(self):
user = User.objects.create_user('developer', 'abc1234')
def test_password_reset(self):
# ...
I didn't create the user and also my URL was wrong. Thanks, #Rvector
class ViewTest(APITestCase):
def test_password_reset(self):
user = User.objects.create_user(username = 'xxx', password= 'xxx',
first_name= 'xxx', last_name = 'xxx',
date_of_birth = 'xxx')
url = '/login/password_reset/'
response = self.client.post(url, {'username': 'xxx', 'xxx': 'abc12345',
'confirm_password': 'xxx'})
user = User.objects.get(username="xxx")
password= "xxx"
self.assertTrue(user.check_password(password))
print(response.status_code)

TypeError at /register 'CustomUser' object is not subscriptable

I am trying to register a new user and getting this error. I tried other similar issues and solutions but not getting headway.
full traceback
Internal Server Error: /register
Traceback (most recent call last):
File "/home/sharhan/DEV/PYTHON/DEVELOPMENT/allauth-tutorial/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/home/sharhan/DEV/PYTHON/DEVELOPMENT/allauth-tutorial/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/sharhan/DEV/PYTHON/DEVELOPMENT/allauth-tutorial/authentication/views.py", line 38, in register_view
CustomUser.objects.get(email__iexact=email)[0]
TypeError: 'CustomUser' object is not subscriptable
register_view in views.py
def register_view(request):
if request.user.is_authenticated:
return redirect('home')
else:
form = CustomUserCreationForm()
if request.method == 'POST':
form = CustomUserCreationForm(request.POST or None)
if form.is_valid():
form.save()
user_name = form.cleaned_data['username']
messages.info(request, 'Account created for ' + user_name)
return redirect('login')
else:
form = CustomUserCreationForm(request.POST)
else:
form = CustomUserCreationForm()
return render(request, 'register.html', {'form': form})
models.py
class CustomUser(AbstractUser):
# add additional fields in here
country = models.CharField(max_length=200, null=True, blank=True)
age = models.IntegerField(default=False, null=True, blank=True)
status = models.BooleanField(default=False, null=True, blank=True)
def __str__(self):
return self.email
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ['username', 'email', 'country', 'age', 'status', 'password1', 'password2']
The second is that I'm also trying to implement functionality which I don't know where to start. If a new user uses the same email again to register, it should throw an error. It should be unique and no double use of email for registration. Username functionality is already implemented by Django so it throws an error if an existing username is already used
Since the first problem has been solved and was due to the IDE not saving the file not properly, this answer only addresses the second part of the question:
The second is that I'm also trying to implement functionality which I don't know where to start. If a new user uses the same email again to register, it should throw an error.
You can make the email field unique by rewriting it in the CustomUser model with a unique=True parameter [Django-doc]. This will enforce uniqness both for the database and for ModelForms, ModelAdmins, ModelSerializers and a lot of other Django tooling that depends on that model:
from django.db import models
from django.utils.translation import gettext_lazy as _
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
# add additional fields in here
# &vellip;
def __str__(self):
return self.email
You can then let Django construct a migration file and migrate the database. Before you can do that, you will however first have to look for duplicates in the already existing database. If that is the case, then you should resolve these first, for example by removing duplicates.

defined a Form class, but django raised exception saying it is not defined

aqszzFollowed instructions as on [the page][1]. to customize user profile while using django-allauth. Django/python saying it can't find the "SignupForm" class definition, which is clearly defined in the file forms.py in the users app as in the code below. Anyone has any idea what's going on?
forms.py
from django import forms
from allauth.account.forms import AddEmailForm, BaseSignupForm
from django.core.exceptions import ValidationError
from django.conf import settings
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class SignupForm(BaseSignupForm):
first_name = forms.CharField(max_length=30, label='Firstname')
last_name = forms.CharField(max_length=150, label='Lastname')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
class MyAddEmailForm(AddEmailForm):
def clean_email(self):
email = super().clean_email()
if self.user.emailaddress_set.count() >= settings.USERS_EMAILS_MAX_COUNT:
raise ValidationError('Number of related emails can be no more than %d.' % settings.USERS_EMAILS_MAX_COUNT)
return email
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = (...)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = (...)
Error message is:
File "D:\Python\Django\m4ever\users\admin.py", line 4, in <module>
from .forms import CustomUserCreationForm, CustomUserChangeForm
File "D:\Python\Django\m4ever\users\forms.py", line 3, in <module>
from allauth.account.forms import AddEmailForm, BaseSignupForm
File "C:\Users\Freedom\Anaconda3\envs\myvenv\lib\site-packages\allauth\account\forms.py", line 261, in <module>
class BaseSignupForm(_base_signup_form_class()):
File "C:\Users\Freedom\Anaconda3\envs\myvenv\lib\site-packages\allauth\account\forms.py", line 249, in _base_signup_form_class
fc_classname))
django.core.exceptions.ImproperlyConfigured: Module "users.forms" does not define a "SignupForm" class
Have been suspecting it's related with circular importing issue. Changed the code a bit to as below, and the issue is gone. The two changes are:
SignupForm now inheriting from normal forms.Form instead of allauth.account.forms.BaseSignupForm
Moved "allauth.account.forms import AddEmailForm" from the beginning of the file, to right before it was needed.
Would still like experts in Python and/or Django-Allauth to clarify exactly what happened.
from django import forms
from django.core.exceptions import ValidationError
from django.conf import settings
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Firstname')
last_name = forms.CharField(max_length=150, label='Lastname')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
from allauth.account.forms import AddEmailForm
class MyAddEmailForm(AddEmailForm):
def clean_email(self):
email = super().clean_email()
if self.user.emailaddress_set.count() >= settings.USERS_EMAILS_MAX_COUNT:
raise ValidationError('Number of related emails can be no more than %d.' % settings.USERS_EMAILS_MAX_COUNT)
return email
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = (...)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = (...)

Django allauth Serialization error custom User model with TimeZoneField

My custom User model have a TimeZoneField:
from timezone_field import TimeZoneField
class User(AbstractBaseUser, PermissionsMixin):
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
email = models.EmailField(_('email address'), unique=True, blank=False, null=False)
username = models.CharField(_('user name'), max_length=128, unique=True, blank=False, null=False)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'))
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
language = models.CharField(_('Language'), choices=settings.LANGUAGES, default=settings.ENGLISH, max_length=2)
timezone = TimeZoneField(verbose_name=_('Timezone'), default='Europe/London')
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
I use django-allauth for registration by Google accounts. When existing user (registered by google email before, not Google Account) trying login by Google Account we have error:
<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> is not JSON serializable
Traceback:
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
149. response = self.process_exception_by_middleware(e, request)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/providers/oauth2/views.py" in view
55. return self.dispatch(request, *args, **kwargs)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/providers/oauth2/views.py" in dispatch
125. return complete_social_login(request, login)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/helpers.py" in complete_social_login
142. return _complete_social_login(request, sociallogin)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/helpers.py" in _complete_social_login
158. ret = _process_signup(request, sociallogin)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/helpers.py" in _process_signup
25. request.session['socialaccount_sociallogin'] = sociallogin.serialize()
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/socialaccount/models.py" in serialize
189. user=serialize_instance(self.user),
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/allauth/utils.py" in serialize_instance
194. return json.loads(json.dumps(data, cls=DjangoJSONEncoder))
File "/usr/lib/python3.4/json/__init__.py" in dumps
237. **kw).encode(obj)
File "/usr/lib/python3.4/json/encoder.py" in encode
192. chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.4/json/encoder.py" in iterencode
250. return _iterencode(o, 0)
File "/webapps/myproject/tmp/venv/lib/python3.4/site-packages/django/core/serializers/json.py" in default
115. return super(DjangoJSONEncoder, self).default(o)
File "/usr/lib/python3.4/json/encoder.py" in default
173. raise TypeError(repr(o) + " is not JSON serializable")
Exception Type: TypeError at /accounts/google/login/callback/
Exception Value: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> is not JSON serializable
What are some ways to serialize a custom field in allauth?
My solution is replace default DefaultSocialAccountAdapter and extension serialize_instance (from allauth.utils) for serializing TimeZoneField. Don't forget set custom adapret in project settings:
SOCIALACCOUNT_ADAPTER = 'myapp.adapter.MySocialAccountAdapter'
Also I replaced pre_social_login for association Social account with Direct account (registered by email) (Thanks elssar for his example: https://stackoverflow.com/a/19443127/4012716)
myapp.adapter.py:
import json
import base64
import logging
from django.db.models import FieldDoesNotExist, FileField
from django.db.models.fields import (BinaryField)
from django.utils import six
from django.core.serializers.json import DjangoJSONEncoder
from django.shortcuts import HttpResponse
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.account.adapter import DefaultAccountAdapter
from allauth.utils import SERIALIZED_DB_FIELD_PREFIX
from allauth.exceptions import ImmediateHttpResponse
from timezone_field import TimeZoneField
from accounts.models import User
logger = logging.getLogger("django")
def my_serialize_instance(instance):
"""Instance serializer supported of serialization of TimeZoneField.
:param instance:
:return:
"""
data = {}
for k, v in instance.__dict__.items():
if k.startswith('_') or callable(v):
continue
try:
field = instance._meta.get_field(k)
if isinstance(field, BinaryField):
v = force_text(base64.b64encode(v))
elif isinstance(field, FileField):
if not isinstance(v, six.string_types):
v = v.name
elif isinstance(field, TimeZoneField):
v = six.text_type(v.zone)
# Check if the field is serializable. If not, we'll fall back
# to serializing the DB values which should cover most use cases.
try:
json.dumps(v, cls=DjangoJSONEncoder)
except TypeError:
v = field.get_prep_value(v)
k = SERIALIZED_DB_FIELD_PREFIX + k
except FieldDoesNotExist:
pass
data[k] = v
return json.loads(json.dumps(data, cls=DjangoJSONEncoder))
class MySocialAccountAdapter(DefaultSocialAccountAdapter):
"""Custom SocialAccountAdapter for django-allauth.
Replaced standard behavior for serialization of TimeZoneField.
Need set it in project settings:
SOCIALACCOUNT_ADAPTER = 'myapp.adapter.MySocialAccountAdapter'
"""
def __init__(self, request=None):
super(MySocialAccountAdapter, self).__init__(request=request)
def pre_social_login(self, request, sociallogin):
# This isn't tested, but should work
try:
emails = [email.email for email in sociallogin.email_addresses]
user = User.objects.get(email__in=emails)
sociallogin.connect(request, user)
raise ImmediateHttpResponse(response=HttpResponse())
except User.DoesNotExist:
pass
except Exception as ex:
logger.error(ex)
def serialize_instance(self, instance):
return my_serialize_instance(instance)

Manager isn't available; User has been swapped for 'pet.Person'

I'm been using the default user model in django for quite a abit and I realize , if I need to further enhance it , I would have to create my own custom User Model in django 1.5 .
I created my custom user model and I have a function which allows users to sign in .
I think my custom user model is incompatible with my function because it wouldn't allow me to do request.user . How can I fix this so I can use request.user again?
views
def LoginRequest(request):
form = LoginForm(request.POST or None)
if request.user.is_authenticated():
username = User.objects.get(username=request.user)
url = reverse('world:Profile', kwargs = {'slug': person.slug})
return HttpResponseRedirect(url)
if request.POST and form.is_valid():
user = form.authenticate_user()
login(request, user)
username= User.objects.get(username=request.user)
person = Person.objects.get(user=request.user)
url = reverse('world:Profile', kwargs = {'slug': person.slug})
return HttpResponseRedirect(url)
return render(request, 'login.html',{'form': form})
models
class PersonManager(BaseUserManager):
def create_user(self, email,date_of_birth, username,password=None,):
if not email:
msg = 'Users must have an email address'
raise ValueError(msg)
if not username:
msg = 'This username is not valid'
raise ValueError(msg)
if not date_of_birth:
msg = 'Please Verify Your DOB'
raise ValueError(msg)
user = self.model(
email=PersonManager.normalize_email(email),username=username,date_of_birth=date_of_birth)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,email,username,password,date_of_birth):
user = self.create_user(email,password=password,username=username,date_of_birth=date_of_birth)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Person(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,db_index=True,)
username = models.CharField(max_length=255, unique=True)
date_of_birth = models.DateField()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'date_of_birth',]
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = PersonManager()
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def __unicode__(self):
return self.email
The problem is that User refers to django.contrib.auth.models.User and now you have got a Custom User pet.Person assuming you have in the settings.py
AUTH_USER_MODEL = "pet.Person"
you have to define User with the Custom User model and you can do this with get_user_model at the top of the file where you use User
from django.contrib.auth import get_user_model
User = get_user_model()
now you will be able to use Custom User model and the problem has been fixed.
For anyone else who might come across this problem, I also solved it by simply doing this on forms.py:
add this at the top of the forms.py file
from .models import YourCustomUser
and then add this to your forms.py CustomUser form:
class SignUpForm(UserCreationForm):
#profile_year = blaaa blaa blaaa irrelevant.. You have your own stuff here don't worry about it
# here is the important part.. add a class Meta-
class Meta:
model = YourCustomUser #this is the "YourCustomUser" that you imported at the top of the file
fields = ('username', 'password1', 'password2', #etc etc, other fields you want displayed on the form)
BIG NOTES, ATTENTION:
This code worked for my case. I have a view for signing users up, I had a problem here and I solved it, I haven't tried it for logging in users.
The include = () part is required, or you can add exclude = (), but you have to have one
Important caveat to update the above solutions...
If you're facing this kind of problem, you've probably tried various solutions around the web telling you to add AUTH_USER_MODEL = users.CustomUser to settings.py and then to add the following code to views.py forms.py and any other file that calls User:
from django.contrib.auth import get_user_model
User = get_user_model()
And then you scratch your head when you get the error:
Manager isn't available; 'auth.User' has been swapped for 'users.User'
Anytime your code references User such as:
User.objects.get()
Cause you know you already put objects = UserManager() in your custom user class (UserManager being the name of your custom manager that extends BaseUserManager).
Well as it turns out doing:
User = get_user_model() # somewhere at the top of your .py file
# followed by
User.objects.get() # in a function/method of that same file
Is NOT equivalent to:
get_user_model().objects.get() # without the need for User = get_user_model() anywhere
Perhaps not intuitive, but it turns out that that in python, executing User = get_user_model() once at the time of import does not then result in User being defined across subsequent calls (i.e. it does not turn User into a "constant" of sorts which you might expect if you're coming from a C/C++ background; meaning that the execution of User = get_user_model() occurs at the time of imports, but is then de-referenced before subsequent called to class or function/method in that file).
So to sum up, in all files that reference the User class (e.g. calling functions or variables such as User.objects.get() User.objects.all() User.DoesNotExist etc...):
# Add the following import line
from django.contrib.auth import get_user_model
# Replace all references to User with get_user_model() such as...
user = get_user_model().objects.get(pk=uid)
# instead of user = User.objects.get(pk=uid)
# or
queryset = get_user_model().objects.all()
# instead of queryset = User.objects.all()
# etc...
Hope this helps save others some time...
In forms.py
# change
from django.contrib.auth.models import User
# to
from django.contrib.auth import get_user_model
Then add the following code at the top
User = get_user_model()
All the solutions provided above did not work in my case. If you using Django version 3.1 there is another solution for you:
In auth/forms, comment out line 10 and change the model in line 104 & 153 to your defined model.