As we all know, django provide an strong User and Authenticate app.
as usually, there is two way to custom the user
use OneToOneField way . we can define a model profile which keep some user information fields. we also can use get_profile method to get the profile. but,there is a problem, if
we get the user profile, we must join the data table. some code demo:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
use AbstractBaseUser, PermissionsMixin . we can custom the user by change the AbstractBaseUser, PermissionsMixin in django--1.5, and must define the UserAdmin.the document:https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#extending-the-existing-user-model
I am very confused and do not know which way is better?
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.
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
)
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
The best way to do this now since Django 1.5 is with custom user models, setting AUTH_USER_MODEL to your user model.
There's no reason to be confused. The docs you link to are correct: you should extend AbstractBaseUser.
Also, you should upgrade to version 1.6.
Related
The custom authentication I wrote follows the instructions from the docs. I am able to register, login, and logout the user, no problem there. Then, when I create a superuser python manage.py createsuperuser, it creates a user in the database, but it does not let me login when I go to the admin page and try to login saying
Please enter the correct email address and password for a staff account. Note that both fields may be case-sensitive.
Here is my code:
models.py:
from __future__ import unicode_literals
from django.db import models
from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, Group, Permission
from django.contrib.auth.models import BaseUserManager
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, MultipleObjectsReturned
from datetime import datetime
from django.contrib.auth.models import PermissionsMixin
import re
class CustomUserManager(BaseUserManager):
def create_user(self, email, password = None):
'''Creates and saves a user with the given email and password '''
if not email:
raise ValueError('Email address is requied.')
user = self.model(email = self.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
''' Creates and saves a superuser with the given email and password '''
user = self.create_user(email, password = password)
user.is_admin = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""
Custom user class
"""
email = models.EmailField(verbose_name = 'email address',unique = True, db_index = True)
# email is the unique field that can be used for identification purposes
first_name = models.CharField(max_length = 20)
last_name = models.CharField(max_length = 30)
joined = models.DateTimeField(auto_now_add = True)
is_active = models.BooleanField(default = True)
is_admin = models.BooleanField(default = False)
is_superuser = models.BooleanField(default = False)
group = models.ManyToManyField(Group, related_name = 'users')
permission = models.ManyToManyField(Permission, related_name = 'users')
objects = CustomUserManager()
USERNAME_FIELD = 'email' # the unique identifier (mandatory) The filed must have unique=True set in its definition (see above)
def get_full_name(self):
return self.email
def get_short_name(self):
return self.first_name
def has_perm(self, perm, obj=None):
''' Does the user have a specific permission'''
return True # This may need to be changed depending on the object we want to find permission for
def has_module_perms(self, app_label):
''' Does the user have permission to view the app 'app_label'? The default answer is yes.
This may be modified later on. '''
return True
#property
def is_staff(self):
''' IS the user a member of staff? '''
return self.is_admin
def __unicode__(self):
return '{user_email}, {user_title} joined on {joined_date}'.format(user_email = self.email,
user_title = self.user_type,
joined_date = self.joined)
In backends.py:
from django.conf import settings
from django.contrib.auth.hashers import check_password
from accounts.models import User
class EmailAuthBackend(object):
''' Custom authentication backend. Allows users to login using their email address '''
def authenticate(self, email=None, password = None):
''' the main method of the backend '''
try:
user = User.objects.get(email = email)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
user = User.objects.get(pk = user_id) # Note that you MUST use pk = user_id in getting the user. Otherwise, it will fail and even though the user is authenticated, the user will not be logged in
if user.is_active:
return user
return None
except User.DoesNotExist:
return None
in admin.py:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from accounts.models import User as CustomUser
class UserCreationForm(forms.ModelForm):
''' A Form for creating new users. Includes all the required field, plus a repeated password.'''
password1 = forms.CharField(label = 'Password', widget = forms.PasswordInput)
password2 = forms.CharField(label = 'Password Confirmation', widget = forms.PasswordInput)
class Meta:
model = CustomUser
fields = ('email',)
def clean_password2(self):
''' Checks that the two password entries match '''
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError('Passwords do NOT match!')
return password2
def save(self, commit = True):
''' Save the provided password in hashed format '''
user = super(UserCreationForm, self).save(commit = False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
''' A form for updating users. Includes all the field on the user, but replaces the password field with admin's password hash display field '''
password = ReadOnlyPasswordHashField()
class Meta:
model = CustomUser
fields = ('email', 'password', 'first_name', 'last_name', 'is_active', 'is_admin')
def clean_password(self):
''' Regardless of what the user provides, return the initial value. This is done here rather than on the field because the field
does not have access to the initial value'''
return self.initial['password']
class UserAdmin(BaseUserAdmin):
''' The form to add and change user instances '''
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the user model.
# These override the defintions on the base UserAdmin
# that reference specific fields on auth.User
list_display = ('email', 'first_name', 'last_name', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal Info',{'fields': ('first_name', 'last_name',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user
add_fieldsets = (
(None, {'classes': ('wide',),
'fields': ('email', 'first_name', 'last_name', 'password1', 'password2')}
),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
# Now, register the new UserAdmin...
admin.site.register(CustomUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
And, finally, in settings.py:
AUTHENTICATION_BACKENDS = ['accounts.backends.EmailAuthBackend',]
So what's missing?
I think the problem is in your EmailAuthBackend. If you add some printing/logging to the backend, you'll find that the login form calls the authenticate method with username and password. This means that email is None, and therefore the user = User.objects.get(email = email) lookup fails.
In your case, the regular ModelBackend will work fine for you, because you have USERNAME_FIELD = 'email'. If you remove AUTHENTICATION_BACKENDS from your settings then the login should work. You can then remove your EmailAuthBackend.
If you wanted to log in users with their cell number and password (and cell_number was not the USERNAME_FIELD, then you would need a custom authentication backend. You would also need a custom authentication form that called authenticate(cell_number=cell_number, password=password). Another example of a custom authentication backed is RemoteUserBackend, which logs in the user based on an environment variable set by the server.
Had the same issue. Instead of password=None, I changed it to password only. And passed 'password=password' , together with 'username=username' as you can see below:
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password):
if not email:
raise ValueError('Please add an email address')
if not username:
raise ValueError('Please add an username')
user = self.model(email=self.normalize_email(
email), username=username, password=password)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(email=self.normalize_email(
email), username=username, password=password)
And as Denis has already said above, make sure to add AUTH_USER_MODEL = 'accounts.User' to settings.py
I created an app authentication, code for models is,
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
# username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
apart from this the changes that i did was added authentication in INSTALLED_APPS and in settings added AUTH_USER_MODEL = 'authentication.Account'
When i create a super-user from shell, its working and inserting in db, but when i try to login into the django admin console,
initially the error was is_staff not found, which i added. Now it is showing invalid username and password for all. I tried that a couple of time generating new one each time. What am i doing wrong ?
EDIT
I am adding the dbs dump that i used to validate if the user was created
sqlite> select * from authentication_account;
1|pbkdf2_sha256$24000$ZRHCXpUI3w0G$fh4z9y5vmYAZ0FEiDr908dJD3ezw62nm3lpkZxUi/Es=||me#rahulbhola.in|||1|0|2016-05-28 21:15:59.292988|2016-05-28 21:15:59.415382
Still login in django defaut admin does not work
You added the is_staff later on which seems to be false by default, so when you migrated the field was created in database with the value false in the is_staff field.
Please make the changes as below and try
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save(using=self.db)
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_staff = True
account.is_admin = True
account.save(using=self.db)
return account
Please not account.save(using=self.db) in the code above and account.is_staff = True in the create admin method.
Please try creating the new user again i prefer using the shell ( python manage.py shell)
from authentication.models import Account
user = Account()
user.create_superuser(username='some_username', password='some_password')
user.save()
Now that the user is created please try logging in using the django admin.
This should work. I had similar issues which is fixed and i am able to create user and login from django admin.
I am assuming that you have created the custom authentication backend already, if not please create
Hope this helps you.
What is the best (easiest?) way to make it a requirement to have an email only for user registration and not make username a requirement in the django-rest-auth framework?
Would I need to write a new User Serializer, for example? Is there a flag setting that I can set True or False to turn off or on this requirement instead?
Thanks
You'll need to write your own AbstractUser and BaseUserManager classes. Fortunately it's pretty simple, add something like this to your app's models:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, blank=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
# This tells Django that this field is absolutely important...
USERNAME_FIELD = 'email'
# ...and username is now optional because it doesn't show up here!
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
class AccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('User must provide an e-mail address')
account = self.model(
email=self.normalize_email(email)
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password=None):
account = self.create_user(email, password)
account.is_admin = True
account.save()
return account
Next, tell Django that this model is your project's new User class. Add the following to your settings.py file:
AUTH_USER_MODEL = 'apiapp.Account'
Once that's done, just migrate:
python manage.py makemigrations
python manage.py migrate
From that point on new users will be represented by this new model (you'd have to handle migrations manually), including anywhere you might use self.request.user!
I'm trying to create a new user, but I get this message:
_get_
raise AttributeError("Manager isn't accessible via %s instances" % type._name_)
AttributeError: Manager isn't accessible via CompanyUser instances
This is what I'm doing:
def obj_create(self, bundle, request=None, **kwargs):
try:
bundle = super(AccountCreateResource, self).obj_create(bundle)
bundle.obj.set_password(bundle.data.get('password'))
bundle.obj.objects.create_user()
except IntegrityError:
raise BadRequest('Username already exists')
I should beable to access the manager. This is my model:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class EmailUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves an EmailUser with the given email and password.
"""
now = timezone.now()
if not email:
raise ValueError('The given email must be set')
email = EmailUserManager.normalize_email(email)
user = self.model(email=email, is_staff=False, is_active=True,
is_superuser=False, last_login=now,
date_joined=now, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **extra_fields):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(email, password, **extra_fields)
user.is_staff = True
user.is_active = True
user.is_superuser = True
user.save(using=self._db)
return user
class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
"""
Abstract User with the same behaviour as Django's default User but
without a username field. Uses email as the USERNAME_FIELD for
authentication.
Use this if you need to extend EmailUser.
Inherits from both the AbstractBaseUser and PermissionMixin.
The following attributes are inherited from the superclasses:
* password
* last_login
* is_superuser
"""
email = models.EmailField(_('email address'), max_length=255,
unique=True, db_index=True)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = EmailUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
abstract = True
def get_full_name(self):
"""
Returns the email.
"""
return self.email
def get_short_name(self):
"""
Returns the email.
"""
return self.email
def email_user(self, subject, message, from_email=None):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email])
class CompanyUser(AbstractEmailUser):
"""
Concrete class of AbstractEmailUser.
"""
company = models.CharField(max_length=100)
pass
You don't show enough of the code or the traceback to understand what's really going on, but anyway: the error message you get means you tried to access the Manager from an instance, and it's not allowed, period - the fact that you have an abstract model is totally irrelevant. You have to access the Manager from the model class itself. Hopefully it's just a matter of replacing instance.objects by type(instance).objects.
It's really not clear what you are trying to do here. This line - the one that's causing the error - makes no sense:
bundle.obj.objects.create_user()
Since you haven't provided the code for bundle, I have to guess that this is a model with an obj ForeignKey, which points to CompanyUser (rather strange naming conventions, though). But as the error says, you can't call objects on an instance, only on the class. But even if you could, the line still doesn't make sense: obj is already a User, so why would you want to create one? And again, create_user takes at least an email parameter, which you're not supplying.
I've been having a heck of a time getting django and django-social-auth to play nicely with a custom user and custom user manager. I've found many posts about this, but nothing seems to be working.
Here are the settings:
LOGIN_URL = '/login/'
LOGIN_ERROR_URL = '/login/error/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'socialauth_associate_complete'
SOCIAL_AUTH_RAISE_EXCEPTIONS = False
SOCIAL_AUTH_FORCE_POST_DISCONNECT = True
SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = True
AUTH_USER_MODEL = 'auth.MyUser'
SOCIAL_AUTH_USER_MODEL = 'auth.MyUser'
FACEBOOK_APP_ID = 'xxxx'
FACEBOOK_API_SECRET = 'xxxx'
FACEBOOK_EXTENDED_PERMISSIONS = ['user_birthday','email','user_about_me','user_education_history','user_events','user_likes','user_subscriptions','user_groups','publish_actions','read_friendlists','user_relationships','user_relationship_details','publish_stream']
AUTHENTICATION_BACKENDS = (
'social_auth.backends.facebook.FacebookBackend',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.get_username',
'social_auth.backends.pipeline.user.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.user.update_user_details'
)
Here is my model
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class MyUserManager(BaseUserManager):
def create_user(self, username, email, password=None):
"""
Creates and saves a User with the given email and password.
"""
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
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='Email address',
max_length=255,
unique=True,
db_index=True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
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
class Meta:
db_table = 'customUser'
From what I can gather the creation of the user is erroring somewhere and forcing the django auth plugin to look for a non existant error backend. This issue resolves itself if I do not use a custom user/manager.
Any assistance on what I may be missing here would be appreciated.