I have one Django user model (with a custom user manager) but two different user types, namely Store and Customer, to handle authentication:
authentication/models.py
class User(AbstractBaseUser, PermissionsMixin):
...
# checks if user is associated with a store object
def is_store(self):
return hasattr(self, 'store')
# checks if user is associated with a customer object
def is_customer(self):
return hasattr(self, 'customer')
stores/models.py
class Store(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.RESTRICT, primary_key=True)
...
def clean(self):
# validates that the selected user doesn't belong to a customer object
if self.user.is_customer():
raise ValidationError({'user': _('This user is already associated with a customer.')})
customers/models.py
class Customer(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE, primary_key=True)
...
def clean(self):
# validates that the selected user doesn't belong to a store object
if self.user.is_store():
raise ValidationError({'user': _('This user is already associated with a store.')})
Now, the stores and customers should use two different websites to login in i.e. stores will use admin.domain.com while customers will simply use domain.com. If a store is logged into admin.domain.com, will it also show that he is logged in when he visits domain.com? If so, how can I prevent this and isolate these two models to specific sites, while using the same authentication model and methods?
One can use permission_classes to accomplish above problem.
DRF has permission_classes attribute which supports list of permission class .
Create 2 permission classes IsShopUser and IsShopCustomer. Both class will be child of IsAuthenticated class which is build in DRF.
from rest_framework.permissions import IsAuthenticated
class ISShopUser(IsAuthenticated):
def has_permission(self, request, view):
"""
if user is a shop user return true
else return false
"""
in each view apply, those permission classes based on user type.
class ShopAPIView(APIView):
permission_classes = [ISShopUser]
It is not recommended to log out users if they enter the wrong API. Simply show an error.
Create 2 mixins StoreUserOnlyMixin and CuustomerUserOnlyMixin inheriting from django TemplateView class. Then in each of then override dispatch method.
from django.views.generic import TemplateView
from django.contrib import auth
from django.http import HttpResponseRedirect
from django.urls import reverse
class CustomerUserOnlyMixin(TemplateView)
def dispatch(self, *args, **kwargs):
if not self.request.user.is_customer():
auth.logout(self.request)
return HttpResponseRedirect(reverse('customer_login_view'))
return super(CustomerUserOnlyMixin, self).dispatch(*args, **kwargs)
And something similar with StoreUserOnlyMixin. After that you can inherit from one of them based on if view should be accessible by Store or Customer
I want to implement autocomplete_fields feature but it doesn't work. I assume it happens because of Proxy model.
So I have Customer Proxy model and PromoCode model. PromoCode has FK to Customer model. And I need to have search field for customers in PromoCode change form. Here are models and admin classes:
class User(AbstractUser):
# bunch of fields
class Customer(User):
class Meta:
proxy = True
class CustomerAdmin(admin.ModelAdmin):
search_fields = ['email',]
admin.site.register(Customer, CustomerAdmin)
class PromoCode(TimeStampedModel):
customer = models.ForeignKey(User, on_delete=PROTECT, null=True, blank=True)
class PromoCodeAdmin(admin.ModelAdmin):
autocomplete_fields = ('customer',)
admin.site.register(PromoCode, PromoCodeAdmin)
This code gives error:
: (admin.E039) An admin for model "User" has to be registered to be referenced by PromoCodeAdmin.autocomplete_fields.
But I can't change model in customer field to Customer, becase when I run migration it breaks with following error:
ValueError: The field coupons.PromoCode.customer was declared with a lazy reference to 'users.customer', but app 'users' doesn't provide model 'customer'
Also I can't register User as admin class, because I don't need it to be registered. I register Customer model.
What can I do to solve such case?
It is not possible (see: https://code.djangoproject.com/ticket/30666). I got around this by registering the User admin, but making it redirect to my custom admin model. I also removed all of the actions on the user admin:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.urls import reverse
admin.site.unregister(User)
#admin.register(User)
class UserAdmin(BaseUserAdmin):
preserve_filters = False
def get_actions(self, request):
actions = super().get_actions(request)
if "delete_selected" in actions:
del actions["delete_selected"]
return actions
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def changelist_view(self, *args, **kwargs):
return HttpResponseRedirect(reverse("admin:core_domainuser_changelist"))
The user is authenticated using allauth. I want to create a profile and set the authenticated user as the owner of the profile. How can I get the user?
Model class:
from django.db import models
from allauth.utils import get_user_model
from courses.models import Course
class Profile(models.Model):
owner = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
courses = models.ManyToManyField(Course, blank=True)
def get_courses_items(self):
return self.courses.all()
def __str__(self):
return self.owner.username
Views:
from rest_framework.generics import CreateAPIView
from profiles.models import Profile
from .serializers import ProfileSerializer
class ProfileCreateView(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
You can get the user from the request with request.user. Then probably you should override the def create method of the CreateAPIView to use that user and create the object.
set your user model in the settings file (base.py) and import it
AUTH_USER_MODEL = 'users.User' #(format is module.user model name)
from django.conf import settings
user = models. OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
method 2
Override the get_create method of the ProfileView. Every authenticated request has a request.user object in it which represents the user making the request. To get the user's id with this you just run request.user.id
class ProfileCreateView(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
def create(self, request, *args, **kwargs):
user = User.objects.get(id=request.user.id)
# create the profile and save it
...
in your serializers you can also get the current user this way
from rest_framework.serializers import CurrentUserDefault, PrimaryKeyRelatedField
class ProfileModelSerializer(ModelSerializer):
user = PrimaryKeyRelatedField(read_only=True, default=CurrentUserDefault())
class Meta:
...
I don't know how the remainder of your setup is but any or a combination of these works
I am stuck at user registration, I actually intends to have different profile types. While registration I am unable to set UserProfile while creating a user. I am using UserCreationForm. code in my files are as following.
from django.contrib.auth.forms import UserCreationForm
from registration.forms import RegistrationForm
from django import forms
from django.contrib.auth.models import User
from accounts.models import UserProfile
from django.utils.translation import ugettext_lazy as _
from person.models import Person
from pprint import pprint
class UserRegistrationForm(UserCreationForm):
#email = forms.EmailField(label = "Email")
fullname = forms.CharField(label = "Full name")
class Meta:
model = User
fields = ("email","fullname","password1","password2" )
def __init__(self, *args, **kwargs):
super(UserRegistrationForm, self).__init__(*args, **kwargs)
del self.fields['username']
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return self.cleaned_data['email']
def save(self, commit=True):
user = super(UserRegistrationForm, self).save(commit=False)
#user_profile=user.set_profile(profile_type="Person")
UserProfile.profile.person.full_name = self.cleaned_data["fullname"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
class CompanyRegistrationForm(UserCreationForm):
email=forms.EmailField(label="Email")
class UserProfileForm(forms.ModelForm):
class Meta:
model=UserProfile
exclude=('user',)
accounts/models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user=models.OneToOneField(User)
meta_keywords=models.CharField("Meta Keywords",max_length=255,
help_text="Comma delimited set of keywords of meta tag")
meta_description=models.CharField("Meta Description",max_length=255,
help_text='Content for description meta tag')
def __unicode__(self):
return "User Profile for: "+self.username
class Meta:
ordering=['-id']
views.py
from django.contrib.auth.forms import UserCreationForm
from django.template import RequestContext
from django.shortcuts import render_to_response,get_object_or_404
from django.core import urlresolvers
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from accounts.forms import UserRegistrationForm, UserProfileForm
#from accounts.forms import UserProfile
def register(request,template_name="account/register.html"):
if request.method=='POST':
postdata=request.POST.copy()
form=UserRegistrationForm(postdata)
user_profile=UserProfileForm(postdata)
if form.is_valid():
form.save()
un=postdata.get('username','')
pw=postdata.get('password','')
from django.contrib.auth import login,authenticate
new_user=authenticate(username=un,password=pw)
if new_user and new_user.is_active:
login(request,new_user)
url=urlresolvers.reverse('dashboard')
return HttpResponseRedirect(url)
else:
form=UserRegistrationForm()
page_title="User Registration"
return render_to_response(template_name,locals(),context_instance=RequestContext(request))
#login_required
def dashboard(request):
pass
#login_required
def settings(request):
pass
As I am using multiple profiles so following is code of one of those profiles' models.py:
from django.db import models
from django.contrib.auth.models import User
from accounts.models import UserProfile
class Person(UserProfile):
skills=models.CharField(max_length=100)
fullname=models.CharField(max_length=50)
short_description=models.CharField(max_length=255)
is_online=models.BooleanField(default=False)
tags=models.CharField(max_length=50)
profile_pic=models.ImageField(upload_to="person_profile_images/")
profile_url=models.URLField()
date_of_birth=models.DateField()
is_student=models.BooleanField(default=False)
current_designation=models.CharField(max_length=50)
is_active_jobseeker=models.BooleanField(default=True)
current_education=models.BooleanField(default=True)
class Meta:
db_table='person'
My profile auth in settings.py
AUTH_PROFILE_MODULE='accounts.UserProfile'
Here is a file that also I used after looking at some other place, profile.py:
from accounts.models import UserProfile
from accounts.forms import UserProfileForm
from person.models import Person
from company.models import Company
def retrieve(request,profile_type):
try:
profile=request.user.get_profile()
except UserProfile.DoesNotExist:
if profile_type=='Person':
profile=Person.objects.create(user=request.user)
else:
profile=Company.objects.create(user=request.user)
profile.save()
return profile
def set(request,profile_type):
profile=retrieve(request,profile_type)
profile_form=UserProfileForm(request.POST,instance=profile)
profile_form.save()
I am new and confuse, have seen documentation also. Also saw other solutions at stackoverflow.com but didn't find any solution of my problem. So please tell if you find anything helpful for me. It doesn't seems to be a big problem but as I am new to it so it is a problem for me.
Multiple profile types won't work with the OneToOne relation that is required by Django profile mechanism. I suggest you keep a single profile class containing data common to all profile types and you store type-specific data in a separate set of classes that you link to your profile class using a generic relation.
EDIT:
Thanks for the clarification. Looking at your code again today it seems that you might indeed be able to accomplish what your trying to do with model inheritance. I think the problem is in the save() method of UserRegistrationForm. Try something like this:
def save(self, commit=True):
user = super(UserRegistrationForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
person = Person(user=user)
person.full_name = self.cleaned_data["fullname"]
person.save()
return user
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)