Django form for creating users with different permissions - django

I'm working on a Django system where there are four levels of users:
1. Basic (access only)
2. Admin (can update/change/delete)
3. Gatekeeper (can only create Admin users, cannot update/change/delete)
4. Developer (true superuser)
I think I have the permissions somewhat figured out:
from django.conf import settings
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
from django.core.validators import RegexValidator
from django.db import models
from django.db.models.signals import post_save
class CustomUserManager(BaseUserManager):
"""Customer User."""
def create_user(self, email, password=None):
"""Creates and saves a user."""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_admin(self, email):
"""Creates and saves an admin user with a temporary password."""
user = self.create_user(
email,
password=BaseUserManager.make_random_password(self, 12)
)
user.is_admin = True
user.save(using=self.db)
return user
def create_gatekeeper(self, email, password):
"""Creates and saves a gatekeeper."""
user = self.create_user(
email,
password=password,
)
user.is_admin = True
user.is_gatekeeper = True
user.save(using=self.db)
return user
def create_superuser(self, email, password):
"""Creates and saves a superuser."""
user = self.create_user(
email,
password=password,
)
user.is_admin = True
user.is_gatekeeper = True
user.is_developer = True
user.save(using=self._db)
return user
class CustomUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_gatekeeper = models.BooleanField(default=False)
is_developer = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
# def __str__(self):
# return self.email
def __str__(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
class Meta:
db_table = 'customuser'
verbose_name = 'CustomUser'
However, I'm not making the connection with how to create gatekeepers and admins. I know I can do it via the command line, but I want
a) a form where developers can create gatekeepers
b) a form where gatekeepers can create admins
Since our administrative site will need to be heavily customized, we will not be using the Django Admin and will likely be building our own functionality. How would I go about calling something like create_gatekeeper inside of a Django form? Would subclassing the Django Admin pages and customizing those be a better way to go?

You probabily should work with Groups and add permissions to those groups;;; but in 3 of yours Actors you can use django builtin attributes from User models...
User: You regular user just create as User class with anything else as is_something...
Admin You can use attribute is_staff that attribute comes from User Models, and allow your user to access django admin...
Developer You can create him as Super User, so all permissions is self added to your models
The thing is about gatekeeper, so if you start to create flags as is_admin, is_gatekeeper and etc... you will start to handle multiple attributes, and this is bad ideia, so when you are working with groups you can create User Group, Developer Group (since they are super admin you dont really need to do that), Admin Group (add each permission you want to give to this groups [Eg. Blog Models you can give to him blog add_blog, change_blog and delete_blog, but you can add your custom too]) same as other groups...
Eg.
# List of my Permissions that i want to add to my groups
PERMISSIONS = {
STUDENT : ['add_student', 'change_student',
'delete_student', 'editpreview_student'],
PROJECT: ['add_project', 'change_project', 'delete_project', 'editpreview_project'],
}
# Creating 2 Groups
grupo_admin, created = Group.objects.get_or_create(name='admin')
grupo_teachers, created = Group.objects.get_or_create(name='teacher')
for func_perm in PERMISSIONS [STUDENT]:
perm = Permission.objects.get(codename=func_perm)
grupo_admin.permissions.add(perm) # Adding Student Permission to my Admin Group
grupo_teachers.permissions.add(perm) # Adding Student Permission to my Teachers Group
for func_perm in PERMISSOES[PROJECT]:
perm = Permission.objects.get(codename=func_perm)
grupo_admin.permissions.add(perm) # Adding Project Permission only to my Admin Group
In your views you can check the permission like that
#user_passes_test(lambda u: u.has_perm('myapp.permission_code'))
def some_view(request):
# ...
And your HTML you can check like that
{% if perms.student.change_student %}
<li>
<a href="/admin/student/student/">
<i class="fa fa-graduation-cap" aria-hidden="true"></i>
<span>Students</span>
</a>
</li>
{% endif %}

Related

Using a custom model for Django Admin

I have a custom user model, which an admin model inherits:
class User(AbstractBaseUser, PermissionsMixin):
...
class Staff(User):
is_staff = models.BooleanField(
_("staff status"),
default=True,
help_text=_("Designates whether the user is staff"),
)
...
class Admin(Staff):
is_admin = models.BooleanField(
_("staff status"),
default=True,
help_text=_("Designates whether the user can log into this admin site."),
)
...
As well as a custom backend for authorization
class AdminBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(Admin.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = Admin._default_manager.get_by_natural_key(username)
except Admin.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
Admin().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
...
However, I am unable to log in due to has_permission in admin/sites.py
Is there a way to avoid registering a custom site, as that will increase complexity?

python-social not saving response to custom model

Ive been running into a number of problem in relation to using django's custom model. This one in particular is not raising any errors. For some reason after authenticating via steam and returning to the landing page the database tables for both steamuser_user (custom user) and social_auth_usersocialauth are empty. Nothing is being saved, no errors are being displayed etc.
My custom model which is quite similar to the one on django docs official page is as follows:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
# Create your models here.
class UserManager(BaseUserManager):
def create_user(self, steamid, username, password=None):
if not steamid:
msg = 'User has no Steam ID set'
raise ValueError(msg)
if not username:
msg = 'User has no name set'
raise ValueError(msg)
user = self.model(steamid=steamid,
username=username)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, steamid, username, password):
super_user = self.create_user(steamid=steamid,
username=username,
password=password)
super_user.is_staff = True
super_user.is_admin = True
super_user.is_mod = True
super_user.save(using=self._db)
return super_user
class User(AbstractBaseUser):
steamid = models.CharField(max_length=20, unique=True)
username = models.CharField(max_length=80)
email = models.EmailField(null=True,blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_mod = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
reputation = models.IntegerField(max_length=6, default=0)
USERNAME_FIELD = 'steamid'
objects = UserManager()
def __unicode__(self):
return self.username
def get_full_name(self):
return self.steamid
def get_short_name(self):
return self.username
The settings I've used are as follows:
SOCIAL_AUTH_USER_MODEL = 'steamuser.User'
AUTH_USER_MODEL = 'steamuser.User'
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'social.apps.django_app.context_processors.backends',
'social.apps.django_app.context_processors.login_redirect',
)
AUTHENTICATION_BACKENDS = (
'social.backends.steam.SteamOpenId',
'django.contrib.auth.backends.ModelBackend',
)
#Steam OpenAuth
SOCIAL_AUTH_STEAM_API_KEY = 'B1D7C629D093D4B72577F2F11DE4EBE2'
LOGIN_URL = '/'
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/'
SOCIAL_AUTH_ENABLED_BACKENDS = (
'steam',
)
Any help would be appreciated!
EDIT
Backends steam.py
def get_user_details(self, response):
player = self.get_json(USER_INFO, params={
'key': self.setting('API_KEY'),
'steamids': self._user_id(response)
})
if len(player['response']['players']) > 0:
player = player['response']['players'][0]
details = {'steamid': player.get('steamid'),
'username': player.get('personaname'),
}
else:
details = {}
return details
EDIT 2
Well despite my logical reasoning, I just gave up and created a custom pipeline to create the new steam user as follows:
from django.contrib.auth import get_user_model
def create_steamuser(details, user=None, *args, **kwargs):
if user:
return {'is_new': False}
if not details:
return
try:
steam_user = get_user_model().objects.get(steamid=details['steamid'])
except steam_user.DoesNotExist:
get_user_model().objects.create_user(details['steamid'], details['username'])
return {
'is_new': True,
}
Now I still have the problem where social_user is not being created. I've set the social user model to use my new custom model but there must be something that I am missing.
python-social-auth won't be able to pass the steamid and date_joined parameters to your custom create_user() method in the manager. To make that possible you have three options:
Set =None to those parameters and set some default vaules for them
Override the default create_user pipeline and pass the extra parameters.
Add a custom pipeline function before create_user and fill details with steamid and date_joined, then define SOCIAL_AUTH_STEAM_USER_FIELDS = ('username', 'email', 'steamid', 'date_joined').

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.

Django custom login using own model

I am using Django1.4 with PostgreSQL. I am developing an application in which I have two models i.e. Students, Company.
class students(models.Model):
first_name = models.CharField(**option)
last_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
# Some other attributes for Student models
class company(models.Model):
compnay_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
#Some other attributes for company models
My Requirement:
Student and Company can create a new profile (provide a sign-up form)
Which creating a new profile for Student/Company, username i.e. email id should be unique. i.e. Email id should not exist in Student & Company models.(task completed )
Created 2 sign-In form for Student & Company login.
Issue:
As I am not using or extending User model, I am cannot use django in-built login & authenticate method.
How can I write a custom authentication method which should check user credentials in Student/Company username & password. (Have 2 different Sign-in form for Student & Company)
Please help me.
Thanks for reading my query.
backend.py
class LoginBackend:
def authenticate(self, username=None, password=None, model=None):
if model == "Student":
lookup_model = Student
elif model == "Employer":
lookup_model = Employer
try:
user = lookup_model.objects.get(email=username)
except Exception, e:
return None
return user
views.py
def check_auth(request):
user_object = Student.objects.get(email__iexact = unicode(email))
if check_password(password, user_object.password):
print authenticate(username = email, password = password, model = "Student")
login(request, user_object)
settings.py
AUTHENTICATION_BACKENDS = ("proj.app.backends.LoginBackend",)
Error
AttributeError at /xxx/login/
'Student' object has no attribute 'backend'
Write a custom authentication backend. Read this:
Writing an authentication backend
Handling authorization in custom backends
settings.AUTHENTICATION_BACKENDS
[update]
By writing and registering a custom authentication backend, you just have to use the standard Django authentication patterns. Looking at your sample code, I'm under the impression that you have understood it differently.
Since email is your unique key, I suggest using email for the login key, first check the login/password against Student, and if it fails, check against Company.
from django.contrib.auth.models import User
class JayapalsBackend(object):
def authenticate(self, username=None, password=None):
try:
o = Student.objects.get(email=username, password=password)
except Student.DoesNotExist:
try:
o = Company.objects.get(email=username, password=password)
except Company.DoesNotExist:
return None
return User.objects.get(email=o.email)
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Then just use standard Django decorators:
#login_required
def some_private_view(request):
...

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)