I have the need of create an user account in my application using just the email
and password
fields. Due to this, my custom user model in my models.py is:
I customize the UserManager to create user
from django.contrib.auth.models import BaseUserManager
class UserManager(BaseUserManager):
def _create_user(self, email, password, **extra_fields):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError("Users must have an email address")
email = self.normalize_email(email)
user = self.model(email = email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
And my User model is:
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.utils.translation import ugettext_lazy as _
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, null=True,
help_text=_('Required. Letters, digits and ''@/./+/-/_ only.'),
validators=[RegexValidator(r'^[\w.@+-]+$', _('Enter a valid email address.'), 'invalid')
])
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this 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.'
),
)
objects = UserManager()
USERNAME_FIELD = "email"
class Meta:
db_table = 'auth_user'
verbose_name_plural = 'Usuarios en la plataforma'
def __str__(self):
return "@{}".format(self.email)
In my settings I've add:
AUTH_USER_MODEL = ‘my_app_name.User’
CREATE USERS - UserCreationForm
pre-built class
To create an user, I am using of the UserCreationForm
class pre-built in the django core.
In this class the username field is used such as denoted here
According to the above, in my forms.py I have:
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = get_user_model()
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = get_user_model()
class UserCreateForm(UserCreationForm):
class Meta:
fields = ("email", "password1", "password2",)
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["email"].label = "Email address"
When I try execute the python manage.py makemigrations , I get this traceback output error
bgarcial@elpug ‹ testing ●● › : ~/workspace/ihost_project
[1] % python manage.py makemigrations accounts
Traceback (most recent call last):
File "manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
utility.execute()
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/core/management/__init__.py", line 341, in execute
django.setup()
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/__init__.py", line 27, in setup
apps.populate(settings.INSTALLED_APPS)
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/apps/registry.py", line 115, in populate
app_config.ready()
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/contrib/admin/apps.py", line 23, in ready
self.module.autodiscover()
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/contrib/admin/__init__.py", line 26, in autodiscover
autodiscover_modules('admin', register_to=site)
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/utils/module_loading.py", line 50, in autodiscover_modules
import_module('%s.%s' % (app_config.name, module_to_search))
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 986, in _gcd_import
File "<frozen importlib._bootstrap>", line 969, in _find_and_load
File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 665, in exec_module
File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
File "/home/bgarcial/workspace/ihost_project/accounts/admin.py", line 8, in <module>
from .forms import CustomUserChangeForm, CustomUserCreationForm
File "/home/bgarcial/workspace/ihost_project/accounts/forms.py", line 16, in <module>
class CustomUserCreationForm(UserCreationForm):
File "/home/bgarcial/.virtualenvs/ihost/lib/python3.5/site-packages/django/forms/models.py", line 257, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (username) specified for User
(ihost)
bgarcial@elpug ‹ testing ●● › : ~/workspace/ihost_project
Of course, I am using UserCreationForm
django class core, I am forcing to use the django core functionalities in which the username field is required
How to can I remove the username or modify this?
I know the modify the django core is not recommended, but, how to can I create an user without include the username field making use of UserCreationForm
django class core?
I try override the save method of my form in where I create the users, but I don't have clear the process, I think that the core of my inconvenient is in the use of UserCreationForm
django class core ..
class UserCreateForm(UserCreationForm):
class Meta:
fields = ("email", "password1", "password2",)
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["email"].label = "Email address"
def save(self, commit=True):
user = super(UserCreateForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
# Tell to Django that not check the username
if commit:
user.save()
return user
If somebody can point me in the right direction will be very much appreciated. :)
I've found a solution that works.
anyways, feel free to suggest better solutions!
Like my inconvenient/error was related with the use of the UserCreationForm
class pre-built in the django core which use the username field in their logic, then I proceeded to make the following:
In my forms.py in my class CustomUserCreationForm
is a child of the class UserCreationForm
, I've override/add to Meta
class the attribute fields
, using the email
field instead of username
field.
This question post help me with it.
My class CustomUserCreationForm
stay as follow:
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = get_user_model()
fields = ('email',)
Then, I proceeded to perform my migrations:
[1] % python manage.py makemigrations accounts
SystemCheckError: System check identified some issues:
ERRORS:
<class 'accounts.admin.UserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'accounts.User'.
This error showed me that the username
field is not an attribute of my User
model.
This means that Django follows trying to ask the username field, even though I overwritten the fields
value with the email
field.
Of course this is logic because I am still inherit from UserCreationForm
class pre-built in the django core
Then, I've add the username
field to my User model with null=True attribute, and of this way, the username does not required in the user account creation:
class User(AbstractBaseUser, PermissionsMixin):
# I had add the username field despite that I don't use in my User model
username = models.CharField(_('username'), max_length=30, null=True,
help_text=_('Required. 30 characters or fewer. Letters, digits and ''@/./+/-/_ only.'),
validators=[RegexValidator(r'^[\w.@+-]+$', _('Enter a valid username.'), 'invalid')
])
email = models.EmailField(unique=True, null=True,
help_text=_('Required. Letters, digits and ''@/./+/-/_ only.'),
validators=[RegexValidator(r'^[\w.@+-]+$', _('Enter a valid email address.'), 'invalid')
])
...
Of this way, I execute my migrations
bgarcial@elpug ‹ testing ●● › : ~/workspace/ihost_project
[1] % python manage.py makemigrations accounts
Migrations for 'accounts':
accounts/migrations/0001_initial.py:
- Create model User
(ihost)
bgarcial@elpug ‹ testing ●● › : ~/workspace/ihost_project
python manage.py migrate accounts ...
And my username field still persist in my custom user schema, just that is not required and when I created an user from my UserCreateForm
class which inherit from UserCreationForm
, I can create an user account with just email and password
I don`t know if this is the best approach to address this inconvenient. Feel free to suggest improvements!