Migrate from standard User to Abstract (custom) user in Django - django

I have created a Django Project using the standard User-form. I really need to make use of the email as login (using this guide). The problem is it should be difficult to migrate when I already have used the standard User-form.
Since we are only in the testing stage at the moment, I don't mind wiping the entire database to make this migration.
Having that in mind, that losing data is not an issue, is there a way to make this migration?
EDIT (added some explanation):
Right now I have the "usual"
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm,PasswordChangeForm
class UserRegisterForm(UserCreationForm):
class Meta:
model = User
fields = ["username","email","password1","password2"]
i.e the login consists of a user-name and a password. Say I want to create my own user (here) I can understand that since I already have users in my database, it's going to be difficult.
So the question is; if I'm using the "standard" user model to create a user, and I want to go from that and using email as login (instead of username), how do I do that?

In the Django documentation, they highly recommend setting up a custom user model when starting a new project, even if the default one is sufficient for you. So to use the email as log in, first you need to add a custom user model:
# YourApp/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email = models.EmailField('email address', unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
next, you need to point AUTH_USER_MODEL to the custom user model in settings.py:
# settings.py
AUTH_USER_MODEL = 'YouApp.User'
Also, make sure to update the forms.py and admin.py files:
# YourApp/forms.py
class YourSignUpForm(UserCreationForm):
class Meta:
model = User
fields = ("username", "email", "password1","password2")
# YourApp/admin.py
from YourApp.models import User
admin.site.register(User, UserAdmin)
Finally, you need to delete the database and recreate it. If you are using postgres, for example, do this:
(venv) $ sudo -u postgres psql
postgres=# DROP DATABASE YourDB;
postgres=# CREATE DATABASE YourDB;
and run the migrations:
python manage.py migrate

With this code, you are going to delete the username from your Custom User. So, you can log in with the email instead of the username.
In YOUR_APP/models.py.
from django.contrib.auth.models import AbstractUser, BaseUserManager, Group
from django.conf import settings
from django.db import models
from django.utils.translation import gettext as _
class UserManager(BaseUserManager):
"""
Model manager for User model without username field
"""
use_in_migrations = True
def create_user(self, email, password, **extra_fields):
"""
Create a User with the given email and password
"""
if not email:
raise ValueError('The Email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **extra_fields):
"""
Create a SuperUser with the given email and password
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', 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)
class User(AbstractUser):
"""
Store a custom user
"""
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
class Meta:
db_table = "auth_user"
def __str__(self):
return self.email
class ProxyUser(User):
class Meta:
app_label = "auth"
proxy = True
verbose_name = "User"
verbose_name_plural = "Users"
class ProxyGroup(Group):
class Meta:
app_label = "auth"
proxy = True
verbose_name = "Group"
verbose_name_plural = "Groups"
In YOUR_APP/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
class MyUserCreationForm(UserCreationForm):
"""
Added creation of the user form.
"""
class Meta(UserCreationForm):
model = User
fields = ["email"]
class MyUserChangeForm(UserChangeForm):
"""
Added updating of the user form.
"""
class Meta:
model = User
fields = ["email"]
In YOUR_APP/Admin.py just if you want to use Django-Admin
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext as _
from .models import User, ProxyUser, ProxyGroup
from .forms import MyUserCreationForm, MyUserChangeForm
class MyUserAdmin(UserAdmin):
"""
Django admin to Users (customizing authentication user)
"""
add_form = MyUserCreationForm
form = MyUserChangeForm
model = User
list_display = [
"email", "first_name", "last_name", "is_staff"
]
list_display_links = [
"email"
]
search_fields = [
"email", "first_name", "last_name"
]
fieldsets = [
("Access info", {
"fields": ("email", "password")
}),
("Personal info", {
"fields": ("first_name", "last_name")
}),
("Permissions", {
"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")
}),
("Important dates", {
"fields": ("last_login", "date_joined")
}),
]
add_fieldsets = [
("Access info", {
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
}),
]
ordering = [
"email"
]
admin.register(ProxyUser, MyUserAdmin)
admin.register(ProxyGroup)
In settings.py you need to redirect to the Custom User
AUTH_USER_MODEL = "YOUR_APP.User"
If you already did migrations before doing this. Make sure to delete/drop and create again the database, and delete the migrations folders and pycache folders of your project to avoids issues.
Finally, do the migrations
python manage.py migrate YOUR_APP
If there is some kind of problems with the migration, you can try with the following code instead of the previous
python manage.py migrate YOUR_APP
python manage.py migrate auth
python manage.py migrate contenttypes
python manage.py migrate admin
python manage.py migrate sessions
I hope this help you.

Related

Django allauth Configuration

I have implemented Django allauth as a method of authenticating users. I am using Django allauth in an attempt to try and handle multi user authentication system. Now when I omit "username" field in my forms.py file, Django complains of the integrity error as below.
IntegrityError at /accounts/accounts/student
UNIQUE constraint failed: accounts_user.username
Request Method: POST
Request URL: http://127.0.0.1:8000/accounts/accounts/student
Django Version: 4.1
Exception Type: IntegrityError
Exception Value:
UNIQUE constraint failed: accounts_user.username
Exception Location: /home/offensive/Desktop/Target/.venv/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py, line 357, in execute
Raised during: accounts.views.StudentSignUpView
Python Executable: /home/offensive/Desktop/Target/.venv/bin/python
Python Version: 3.8.10
Python Path:
['/home/offensive/Desktop/Target',
'/usr/lib/python38.zip',
'/usr/lib/python3.8',
'/usr/lib/python3.8/lib-dynload',
'/home/offensive/Desktop/Target/.venv/lib/python3.8/site-packages']
Server time: Mon, 13 Feb 2023 14:14:23 +0000
below is my forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, PasswordChangeForm
from django.contrib.auth import get_user_model
from django.db import transaction
from .models import Student, User, Writter
class StudentSignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = get_user_model()
fields = (
"email",
# "username",
)
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.email = self.cleaned_data.get('email')
user.is_student = True
user.save()
student = Student.objects.create(user=user)
return user
class WritterSignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
phone = forms.CharField(required=True)
address = forms.CharField(required=True)
class Meta:
model = get_user_model()
fields = (
"email",
# "username",
"phone",
"address",
)
By default, I think Django allauth should populate username field automatically using the email address which I already made "required". Below is the Allauth configurations on my settings.py file.
..................
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
)
......
SITE_ID = 1
ACCOUNT_USER_MODEL_USERNAME_FIELD = "email"
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
# # ACCOUNT_EMAIL_VERIFICATION = "mandatory"
LOGIN_REDIRECT_URL = "/dashboard"
ACCOUNT_LOGOUT_REDIRECT = "/"
ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
ACCOUNT_SIGNUP_REDIRECT_URL = "/dashboard"
# ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 3
......
Delete your line ACCOUNT_USER_MODEL_USERNAME_FIELD = "email". It seems you don't have a custom user model, and this setting is intended to tell Django which fieldname in a custom user model should be treated as what it expects to see as username. It's not related to telling Django which fieldname to use for validation.
Try using signals. Create a signals.py file in the same folder you have your forms.py for accounts:
from django.contrib.auth import get_user_model
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.conf import settings
User = get_user_model()
#receiver(pre_save,sender=User)
def set_username(sender,instance,**kwargs):
instance.username = instance.email
If that doesn't work, and if you haven't already, you should probably look into creating a custom user model, which is the recommended approach anyway.
This article is a good starting point, and this answer goes into how to get rid of the username field completely.

Django user profile style auth with Djoser

I'm trying to implement authentication with djoser. Since I haven't extended my AbstractBaseUser from Django at start of my project I decided to use a one-to-one profile relation pattern. I have a seperated user app and Here are my related codes:
# models.py (user and profile models)
from django.db import models
# Create your models here.
from django.db import models
from django.contrib.auth.models import User as BaseUser
class User(BaseUser):
#property
def mobile_number(self):
return self.profile.mobile_number
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
mobile_number = models.CharField(max_length=11, unique=True)
def __str__(self):
return self.user.username
# settings.py (djoser settings)
...
DJOSER = {
"SERIALIZERS":{
"user": "user.serializers.UserSerializer"
"current_user": "user.serializers.UserSerializer"
"user_create": "user.serializers.UserCreateSerializer"
}
...
}
...
# user.serializers.py
from rest_framework import serializers
from .models import UserProfile
from .models import User
from djoser.serializers import UserCreateSerializer as BaseUserCreateSerializer
from djoser.serializers import UserSerializer as BaseUserSerializer
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
exclude = ["user"]
class UserSerializer(serializers.ModelSerializer):
mobile_number = serializers.CharField(required=True, allow_blank=False)
class Meta:
model = User
fields = ["username", "email", "first_name", "last_name", "mobile_number"]
class UserCreateSerializer(BaseUserCreateSerializer):
mobile_number = serializers.CharField(required=True, allow_blank=False)
class Meta(BaseUserCreateSerializer.Meta):
fields = ["username", "email", "first_name", "last_name", "mobile_number"]
def create(self, validated_data):
mobile_number = validated_data.pop("mobile_number")
user = super.create(**validated_data)
UserProfile.objects.create(user=user, mobile_number=mobile_number)
return user
What I'm trying to do here is
Skip migrations with adding just a property to django User model.
Extending Djoser serializer to just create the relation in create step.
But when i try to create user with post request to djoser endpoint (auth/users/create) with body like this:
{
"username": "user",
"email": "user#examssple.com",
"first_name": "hatef",
"last_name": "madani",
"mobile_number": "01236549879"
}
I receive this error: "User() got an unexpected keyword argument 'mobile_number'". Any help on current approach or better one would be great.
I think you should follow this official documentation: https://docs.djangoproject.com/en/3.1/topics/auth/customizing/
your custom User should extends AbstractBaseUser or AbstractUser and you need customize a UserManager with create_user(..) method

(admin.E116) The value of 'list_filter[3]' refers to 'groups', which does not refer to a Field

While trying to create my own user model and admin, the test gives the following error.
: (admin.E019) The value of 'filter_horizontal[0]' refers to 'groups', which is not an attribute of 'account.User'.
: (admin.E019) The value of 'filter_horizontal[1]' refers to 'user_permissions', which is not an attribute of 'account.User'.
: (admin.E116) The value of 'list_filter[1]' refers to 'is_superuser', which does not refer to a Field.
: (admin.E116) The value of 'list_filter[3]' refers to 'groups', which does not refer to a Field.
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class UserManager(BaseUserManager):
def create_user(self,username,email,password=None, **extra_fields):
user=self.model(
username=username.lower(),
email=self.normalize_email(email),
**extra_fields)
#user name is converted into lowercase
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,username,email,password=None):
user = self.create_user(
username,email
)
user.set_password(password)
user.is_superuser=True
user.is_staff =True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
username=models.CharField(max_length=255,unique=True)
email=models.EmailField(max_length=255,unique=True)
password=models.CharField(max_length=255)
is_staff =models.BooleanField(default=False)
is_active=models.BooleanField(default=True)
objects=UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from . import models
# Register your models here.''''
class UserAdmin(BaseUserAdmin):
ordering = ['id']
list_display=['username','email']
admin.site.register(models.User,UserAdmin)
admintest.py
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from django.urls import reverse
class AdminTests(TestCase):
def setUp(self):
self.client = Client()
self.admin_user = get_user_model().objects.create_superuser(
username='admin',
password='123',
email='fda#gmail.com'
)
self.client.force_login(self.admin_user)
self.user = get_user_model().objects.create_user(
username='mantis',
password='123',
email='111'
)
def test_users_listed(self):
"""Test that users are listed on the user page"""
url = reverse('admin:core_user_changelist')
res = self.client.get(url)
self.assertContains(res, self.user.username)
self.assertContains(res, self.user.email)
You simply need to add PermissionsMixin in your class User(AbstractBaseUser) such so User(AbstractBaseUser, PermissionsMixin)
You can import it with following: from django.contrib.auth.models import PermissionsMixin
if you look at django docs
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#module-django.contrib.admin
in the Note they said you need to add your custom fields to fieldsets (for fields to be used in editing users) and to add_fieldsets (for fields to be used when creating a user).
so you need to add these fields to your UserAdmin class
For anyone who come across the same issue, simply import the permission mixin as so: from django.contrib.auth.models import PermissionsMixin and sub-class it with your custom user model (along side the AbstractBaseUser class).
Also, there's no need to include email, as the authentication token, in the REQUIRED_FIELDS array. This is because the USERNAME_FIELD field is already set to email and it's always prompted as required.
Another error you may come across is the (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of '<app.CustomUserModel>'. This is because the USERNAME_FIELD in the custom model was re-assigned to use email instead. To fix, add ordering=("email",) to the registered custom model fieldsets in admin.py.
There is something to note before doing anything: as in my case, you have to make sure there is no method in your model with the same name as the fields which throw E116. (Python, right?)
In this example, make sure you don't have any method named is_superuser and groups in your model.

Custom User Model error

I'm trying to set up my custom user model in Django. The reason is that I want to use email as the username, and remove the username field entirely. I've run into a error, that I just can't figure out.
Manager isn't available; User has been swapped for 'app.MyUser'
Exception Location: .../django/db/models/manager.py in __get__, line 256
Python Version: 2.7.3
Python Path:
[...project specific files,
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']
I've googled like crazy, but haven't found too many pages about this error message. I have found some pages, with suggestions on how to solve it, but none of the suggestions have worked for me.
My code: I've set the custom user model. I have declared the custom user model AUTH_USER_MODEL = 'app.MyUser' in settings.py. I have also set up a custom UserManager:
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None):
"""
Creates and saves a User with the given email. Note that none of the optional fields gets values in the creation. These fields will have to be filled out later on.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(email=MyUserManager.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None):
"""
Creates and saves a superuser with the the above mentioned attributes
"""
user = self.create_user(email, password=password)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser, PermissionsMixin):
"""
Custom made User model. No username, instead email is used as unique field and index
"""
Genders = (('M', 'Man'), ('K', 'Woman'))
FirstName = models.CharField(max_length=30)
LastName = models.CharField(max_length=40)
Gender = models.CharField(max_length=2, choices=Genders, default='K')
email = models.EmailField(verbose_name='email address', max_length=255, unique=True, db_index=True,)
twitter = models.CharField(max_length=30)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __unicode__(self):
return self.email
objects = MyUserManager()
I've tried to declare to different types of UserAdmins, none of which is making any difference,the first one I tried was;
class MyUserAdmin(UserAdmin):
# The forms to add and change user instances
#form = UserChangeForm
#add_form = FrontpageRegistrationForm
list_display = ('email', 'FirstName', 'LastName', 'Gender', 'twitter')
list_filter = ()
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(MyUser, MyUserAdmin)
I've commented out the two attributes add_form and form because they raised some form errors I wanted to get back to at a later point.
The second UserAdmin was made, after reading about a possible fix here. This didn't help the situation though;
class MyUserAdmin(admin.ModelAdmin):
# The forms to add and change user instances
#form = UserChangeForm
add_form = FrontpageRegistrationForm
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(MyUserAdmin, self).get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults.update({'form': self.add_form,'fields': admin.util.flatten_fieldsets(self.add_fieldsets),})
defaults.update(kwargs)
return super(MyUserAdmin, self).get_form(request, obj, **defaults)
I've also tried deleting all tables in the db with no luck.
I would be eternally greatful to anyone who even looks at the problem. And if any one were to solve this, I would try my best to talk my wife into naming our firstborn after the Avatar that gave me a solution so that I could go on living my life.
EDIT:
I tried setting the AUTH_USER_MODELto mainfolder.app.MyUserI'm sure the "mainfolder" is on the pythonpath. init.py in the app should be correct. The new settings.py gave the following server error; auth.user: AUTH_USER_MODEL is not of the form 'app_label.app_name'.admin.logentry: 'user' has a relation with model smartflightsearch.SFSdb.MyUser, which has either not been installed or is abstract.registration.registrationprofile: 'user' has a relation with model, which has either not been installed or is abstract. A new clue I don't know how to interpret..
TL;DR: Use the code from the Solution part at the end of the following answer.
Longer explanation: You see, as of Django 1.5, it's not enough to subclass Django's UserAdmin to be able to interact with swappable user models: you need to override respective forms as well.
If you jump to django.contrib.auth.admin source, you'll see that the UserAdmin's form and add_form have these values:
# django/contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
...
form = UserChangeForm
add_form = UserCreationForm
Which point us to forms in django.contrib.auth.forms that do not respect swappable user models:
# django/contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
class UserChangeForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
Solution: So, you should follow a great already existing answer (don't forget to vote it up!) which boils down to this:
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class MyUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
class MyUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
admin.site.register(MyUser, MyUserAdmin)
Hopefully, this would be fixed in the future releases of Django (here's the corresponding ticket in the bug tracker).
When you said you set AUTH_USER_MODEL = 'app.MyUser' I'm assuming your app where is located the MyUser class, have a structure, perharps, like this:
inside the app/ dir: init.py and models.py and stuff..
so inside the models.py you have the MyUser and inside the init.py:
from models import MyUser

Extending the User model with custom fields in Django

What's the best way to extend the User model (bundled with Django's authentication app) with custom fields? I would also possibly like to use the email as the username (for authentication purposes).
I've already seen a few ways to do it, but can't decide on which one is the best.
The least painful and indeed Django-recommended way of doing this is through a OneToOneField(User) property.
Extending the existing User model
…
If you wish to store information related to User, you can use a one-to-one relationship to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
That said, extending django.contrib.auth.models.User and supplanting it also works...
Substituting a custom User model
Some kinds of projects may have authentication requirements for which Django’s built-in User model is not always appropriate. For instance, on some sites it makes more sense to use an email address as your identification token instead of a username.
[Ed: Two warnings and a notification follow, mentioning that this is pretty drastic.]
I would definitely stay away from changing the actual User class in your Django source tree and/or copying and altering the auth module.
Note: this answer is deprecated. see other answers if you are using Django 1.7 or later.
This is how I do it.
#in models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
#other fields here
def __str__(self):
return "%s's profile" % self.user
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
#in settings.py
AUTH_PROFILE_MODULE = 'YOURAPP.UserProfile'
This will create a userprofile each time a user is saved if it is created.
You can then use
user.get_profile().whatever
Here is some more info from the docs
http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
Update: Please note that AUTH_PROFILE_MODULE is deprecated since v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module
Well, some time passed since 2008 and it's time for some fresh answer. Since Django 1.5 you will be able to create custom User class. Actually, at the time I'm writing this, it's already merged into master, so you can try it out.
There's some information about it in docs or if you want to dig deeper into it, in this commit.
All you have to do is add AUTH_USER_MODEL to settings with path to custom user class, which extends either AbstractBaseUser (more customizable version) or AbstractUser (more or less old User class you can extend).
For people that are lazy to click, here's code example (taken from docs):
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class MyUserManager(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=MyUserManager.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, date_of_birth, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
u = self.create_user(username,
password=password,
date_of_birth=date_of_birth
)
u.is_admin = True
u.save(using=self._db)
return u
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
Since Django 1.5 you may easily extend the user model and keep a single table on the database.
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _
class UserProfile(AbstractUser):
age = models.PositiveIntegerField(_("age"))
You must also configure it as current user class in your settings file
# supposing you put it in apps/profiles/models.py
AUTH_USER_MODEL = "profiles.UserProfile"
If you want to add a lot of users' preferences the OneToOneField option may be a better choice thought.
A note for people developing third party libraries: if you need to access the user class remember that people can change it. Use the official helper to get the right class
from django.contrib.auth import get_user_model
User = get_user_model()
There is an official recommendation on storing additional information about users.
The Django Book also discusses this problem in section Profiles.
The below one is another approach to extend an User.
I feel it is more clear,easy,readable then above two approaches.
http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
Using above approach:
you don't need to use
user.get_profile().newattribute to access the additional information
related to the user
you can just directly access
additional new attributes via
user.newattribute
You can Simply extend user profile by creating a new entry each time when a user is created by using Django post save signals
models.py
from django.db.models.signals import *
from __future__ import unicode_literals
class UserProfile(models.Model):
user_name = models.OneToOneField(User, related_name='profile')
city = models.CharField(max_length=100, null=True)
def __unicode__(self): # __str__
return unicode(self.user_name)
def create_user_profile(sender, instance, created, **kwargs):
if created:
userProfile.objects.create(user_name=instance)
post_save.connect(create_user_profile, sender=User)
This will automatically create an employee instance when a new user is created.
If you wish to extend user model and want to add further information while creating a user you can use django-betterforms (http://django-betterforms.readthedocs.io/en/latest/multiform.html). This will create a user add form with all fields defined in the UserProfile model.
models.py
from django.db.models.signals import *
from __future__ import unicode_literals
class UserProfile(models.Model):
user_name = models.OneToOneField(User)
city = models.CharField(max_length=100)
def __unicode__(self): # __str__
return unicode(self.user_name)
forms.py
from django import forms
from django.forms import ModelForm
from betterforms.multiform import MultiModelForm
from django.contrib.auth.forms import UserCreationForm
from .models import *
class ProfileForm(ModelForm):
class Meta:
model = Employee
exclude = ('user_name',)
class addUserMultiForm(MultiModelForm):
form_classes = {
'user':UserCreationForm,
'profile':ProfileForm,
}
views.py
from django.shortcuts import redirect
from .models import *
from .forms import *
from django.views.generic import CreateView
class AddUser(CreateView):
form_class = AddUserMultiForm
template_name = "add-user.html"
success_url = '/your-url-after-user-created'
def form_valid(self, form):
user = form['user'].save()
profile = form['profile'].save(commit=False)
profile.user_name = User.objects.get(username= user.username)
profile.save()
return redirect(self.success_url)
addUser.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="." method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Add</button>
</form>
</body>
</html>
urls.py
from django.conf.urls import url, include
from appName.views import *
urlpatterns = [
url(r'^add-user/$', AddUser.as_view(), name='add-user'),
]
Extending Django User Model (UserProfile) like a Pro
I've found this very useful: link
An extract:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
It's very easy in Django version 3.0+ (If you are NOT in the middle of a project):
In models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
extra_field=models.CharField(max_length=40)
In settings.py
First, register your new app and then below AUTH_PASSWORD_VALIDATORS
add
AUTH_USER_MODEL ='users.CustomUser'
Finally, register your model in the admin, run makemigrations and migrate, and it will be completed successfully.
Official doc: https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#substituting-a-custom-user-model
It's too late, but my answer is for those who search for a solution with a recent version of Django.
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
extra_Field_1 = models.CharField(max_length=25, blank=True)
extra_Field_2 = models.CharField(max_length=25, blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
you can use it in templates like this:
<h2>{{ user.get_full_name }}</h2>
<ul>
<li>Username: {{ user.username }}</li>
<li>Location: {{ user.profile.extra_Field_1 }}</li>
<li>Birth Date: {{ user.profile.extra_Field_2 }}</li>
</ul>
and in views.py like this:
def update_profile(request, user_id):
user = User.objects.get(pk=user_id)
user.profile.extra_Field_1 = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit...'
user.save()
New in Django 1.5, now you can create your own Custom User Model (which seems to be good thing to do in above case). Refer to 'Customizing authentication in Django'
Probably the coolest new feature on 1.5 release.
Here I tried to explain how to extend Django's Default user model with extra fields
It's very simple just do it.
Django allows extending the default user model with AbstractUser
Note:- first create an extra field model which you want to add in user model then run the command python manage.py makemigrations and python manage.py migrate
first run ---> python manage.py makemigrations then
second run python manage.py migrate
Step:- create a model with extra fields which you want to add in Django default user model (in my case I created CustomUser
model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
mobile_no = models.IntegerField(blank=True,null=True)
date_of_birth = models.DateField(blank=True,null=True)
add in settings.py name of your model which you created in my case CustomUser is the user model. registred in setttings.py to make it the default user model,
#settings.py
AUTH_USER_MODEL = 'myapp.CustomUser'
finally registred CustomUser model in admin.py
#admin.py
#admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
list_display = ("username","first_name","last_name","email","date_of_birth", "mobile_no")
then run command python manage.py makemigrations
then python manage.py migrate
then python manage.py createsuperuser
now you can see your model Default User model extended with (mobile_no ,date_of_birth)
This is what i do and it's in my opinion simplest way to do this. define an object manager for your new customized model then define your model.
from django.db import models
from django.contrib.auth.models import PermissionsMixin, AbstractBaseUser, BaseUserManager
class User_manager(BaseUserManager):
def create_user(self, username, email, gender, nickname, password):
email = self.normalize_email(email)
user = self.model(username=username, email=email, gender=gender, nickname=nickname)
user.set_password(password)
user.save(using=self.db)
return user
def create_superuser(self, username, email, gender, password, nickname=None):
user = self.create_user(username=username, email=email, gender=gender, nickname=nickname, password=password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(PermissionsMixin, AbstractBaseUser):
username = models.CharField(max_length=32, unique=True, )
email = models.EmailField(max_length=32)
gender_choices = [("M", "Male"), ("F", "Female"), ("O", "Others")]
gender = models.CharField(choices=gender_choices, default="M", max_length=1)
nickname = models.CharField(max_length=32, blank=True, null=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
REQUIRED_FIELDS = ["email", "gender"]
USERNAME_FIELD = "username"
objects = User_manager()
def __str__(self):
return self.username
Dont forget to add this line of code in your settings.py:
AUTH_USER_MODEL = 'YourApp.User'
This is what i do and it always works.
Simple and effective approach is
models.py
from django.contrib.auth.models import User
class CustomUser(User):
profile_pic = models.ImageField(upload_to='...')
other_field = models.CharField()
Currently as of Django 2.2, the recommended way when starting a new project is to create a custom user model that inherits from AbstractUser, then point AUTH_USER_MODEL to the model.
Source: https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project
Try this:
Create a model called Profile and reference the user with a OneToOneField and provide an option of related_name.
models.py
from django.db import models
from django.contrib.auth.models import *
from django.dispatch import receiver
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile')
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
try:
if created:
Profile.objects.create(user=instance).save()
except Exception as err:
print('Error creating user profile!')
Now to directly access the profile using a User object you can use the related_name.
views.py
from django.http import HttpResponse
def home(request):
profile = f'profile of {request.user.user_profile}'
return HttpResponse(profile)