I am a beginner in Django.
I am trying to build an app with user authentication. However I want extra fields like country and phone number, and I don’t want any username field (I want the phone number to act as the username), so I built a custom user class.
There are questions that have already been asked that have the same error, but they are not exactly relevant to my use case and the solutions don’t work for me.
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractBaseUser
from django.conf import settings
from django_countries.fields import CountryField
# Create your models here.
class UserProfile(AbstractBaseUser):
user = models.OneToOneField(User, on_delete = models.DO_NOTHING)
phone_number = models.CharField(max_length = 16, unique = True, blank = False, null = False)
country = CountryField()
uid = models.UUIDField(
default = None,
blank = True,
null = True,
unique = True,
)
USERNAME_FIELD = "uid"
REQUIRED_FIELDS = ['phone_number', 'country']
forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import UserProfile
from django_countries.fields import CountryField
# Create your forms here..
class NewUserForm(UserCreationForm):
phone_number = forms.RegexField(max_length = 16, regex = r'^\+?1?\d{9,15}$')
country = CountryField()
class Meta:
model = UserProfile
fields = ("phone_number", "country", "password1", "password2")
def save(self, commit = True):
user = super(NewUserForm, self).save(commit = False)
user.phone_number = self.cleaned_data['phone_number']
user.username = user.phone_number
user.country = self.cleaned_data['country']
if commit:
user.save()
return user
settings.py:
"""
Django settings for app project.
Generated by 'django-admin startproject' using Django 4.0.1.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-pu46*rr%pr(^utidu^bob6o#f*s4w5ig#$fmc$f@ga45+p2wzc'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'inside.apps.InsideConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'phonenumber_field',
]
AUTH_USER_MODEL = 'inside.UserProfile'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'app.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'app.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Kolkata'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Django throws this error:
Watching for file changes with StatReloader
Performing system checks...
Exception in thread django-main-thread:
Traceback (most recent call last):
File "C:\Users\Kanav\AppData\Local\Programs\Python\Python39\lib\threading.py", line 973, in _bootstrap_inner
self.run()
File "C:\Users\Kanav\AppData\Local\Programs\Python\Python39\lib\threading.py", line 910, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Kanav\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "C:\Users\Kanav\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 124, in inner_run
self.check(display_num_errors=True)
File "C:\Users\Kanav\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\base.py", line 488, in check
raise SystemCheckError(msg)
django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:
ERRORS:
inside.UserProfile.user: (fields.E301) Field defines a relation with the model 'auth.User', which has been swapped out.
HINT: Update the relation to point at 'settings.AUTH_USER_MODEL'.
System check identified 1 issue (0 silenced).
>Solution :
You are defining a relationship to the default User Model, which you are not using anymore as you have created a custom user model. Remove the one_to_one_field to avoid this error
And to further errors you have to create a custom manager
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractBaseUser
from django.conf import settings
from django_countries.fields import CountryField
# Create your models here.
class UserProfile(AbstractBaseUser):
# Remove this as you no longer using the default User Model.
# user = models.OneToOneField(User, on_delete = models.DO_NOTHING)
phone_number = models.CharField(max_length = 16, unique = True, blank = False, null = False)
country = CountryField()
# As you are using UID as your username field
# it is not safe to make it blank and null true
uid = models.UUIDField(
default = None,
blank = True,
null = True,
unique = True,
)
USERNAME_FIELD = "uid"
REQUIRED_FIELDS = ['phone_number', 'country']
# Create your custom user manager
objects = YOUR_CUSTOM_USER_MANAGER()
Note: As you are using uid as your username field, you have to write a custom authentication backend so that you can use the phone number to authenticate the user, otherwise you have to use the phone number as a username field if you don’t want to create a custom authentication backend.