I am building an app with multiple roles defined through Django Groups.
I started with a custom user model, defined as below.
I am seeing a weird difference in the groups and permissions use when using a custom user model, like the inheritance is missing something.
I would like to use a custom user model so I don't use username but I also need multiple groups and permissions in my application.
from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, BaseUserManager, PermissionsMixin
import random
import string
from slugify import slugify
# Create your models here.
class MyAccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError("You 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_superuser(self, email, password):
user = self.create_user(
email=self.normalize_email(email),
)
user.set_password(password)
user.is_admin=True
user.is_staff=True
user.is_superuser=True
user.save(using=self._db)
return user
#custom user account model
class User_Account(AbstractUser):
email = models.EmailField(verbose_name='email', max_length=60, unique = True)
date_joined = models.DateTimeField(verbose_name="Date Joined", auto_now_add=True)
last_login = models.DateTimeField(verbose_name="Last Login", auto_now=True)
username = models.CharField(max_length=100, null=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = MyAccountManager()
def __str__(self):
return self.email
When I create a new group with the custom user model, the group gets automatically assigned to all created users. I reproduced this behavior both programmatically and through the Django Admin.
When I use the default user model the group creation doesn't assign groups to all users automatically.
I also discovered that when using a custom user model the Django Admin for the users is not the same (the group and permission assignment fields are incomplete - screenshots below)
Weird incomplete Django Admin interface with groups and permissions missing the available fields
Normal Django Admin interface with group and permission assignment as expected - default user model
I managed to fix the issue in the Admin panel. It seems that it's a visual rendering problem caused by a wrong Admin class.
The error was caused by the following:
filter_horizontal = ()
list_filter = ()
fieldsets = ()
I have actually added the proper parameters in the class above but forgot to comment out/remove these lines. Works properly after commenting them out.
Try removing
filter_horizontal = (),
It worked for me.
Want to create a Web RESTful API with Django. For that I'm using Django REST Framework.
What are the necessary steps to get the authentication using a custom User model (subclassing AbstractBaseUser) exposing the endpoints to be used?
1. Create a Custom User Model
The documentation is good when it comes to Specifying a custom user model.
In our models.py, import AbstractBaseUser and BaseUserManager.
Then, create your class, for instances,
class Profiles(AbstractBaseUser):
userId = models.CharField(max_length=36, unique= True)
username = models.CharField(max_lenght=20)
password = models.CharField(max_lenght=256)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
...
Here it's very important to set USERNAME_FIELD and REQUIRED_FIELDS to define which fields are important when logging in.
Also in your custom user model class, you have to create two functions needed to deal with permissions, namely has_perm() and has_module_perms().
2. Create a Custom User Manager
Now the next step is to create a Custom User Manager, which is something recommended by Django.
So we create a class like
class ProfilesManager(BaseUserManager):
...
In that class, one needs to override two methods, namely create_user() and create_superuser(), to define what happens when a user is created and when a superuser is created, respectively.
Then, we need to tell Profiles class with an objects parameter where this ProfilesManager class is.
class Profiles(AbstractBaseUser):
userId = models.CharField(max_length=36, unique= True)
email = models.CharField(max_lenght=20)
password = models.CharField(max_lenght=256)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
...
USERNAME_FIELDS = 'userId'
REQUIRED_FIELDS = ['email']
objects = ProfilesManager()
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
3. Set User Model
Go to your settings.py and add a AUTH_USER_MODEL property, specifying which user model Django should use.
AUTH_USER_MODEL = 'appName.Profiles'
4. Make migrations
To commit the changes made above one has to make the migrations by running
python manage.py makemigrations
5. Register User from REST API
As we have created the model, then we just need to create the serializers, views and urls.
I am trying to allow all active user to login to admin site because as a default just staff and superusers are able to login to admin site.
I've tried to overwrite clean method in my custom crate/change forms but it didn't help
class User(GuardianUserMixin, AbstractUser):
name = models.CharField(_("Name of User"), blank=True, max_length=255)
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
#admin.register(User)
class UserAdmin(GuardedModelAdminMixin, auth_admin.UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
fieldsets = FIELDSETS
list_display = ["username", "name", "is_active", "is_superuser", "is_staff"]
search_fields = ["name", "username"]
class UserChangeForm(forms.UserChangeForm):
class Meta(forms.UserChangeForm.Meta):
model = User
class UserCreationForm(forms.UserCreationForm):
error_message = forms.UserCreationForm.error_messages.update(
{"duplicate_username": _("This username has already been taken.")}
)
class Meta(forms.UserCreationForm.Meta):
model = User
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise
ValidationError(self.error_messages["duplicate_username"])```
There are 2 things that determine whether a user can access the Django Admin:
User.is_staff1 must be set to True
The user will need the appropriate permissions for each model
There are multiple ways to set is_staff to True for all new users. The easiest would probably be to redefine is_staff = models.BooleanField(default=True) on your User model.
If you are not going to use per-model permissions you can also redefine is_superuser = models.BooleanField(default=True) which will give the user all permissions on every model.
Note though that the Django Admin is primarily designed to be used by your developers and site administrators who know what they are doing, and not most regular users.
I have override the default User model in my app with AUTH_USER_MODEL.
Here is the user model I want to use in my app, which is tied closely to a legacy database:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
name = models.CharField(max_length=128, blank=True)
email = models.EmailField(unique=True)
password = models.CharField(max_length=128)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
normalized_email = models.EmailField(unique=True)
However, django complains that fields such as first_name, last_name, etc. are not provided. Is there a way to remove some of the existing fields in the User model? Or does specifying a custom user model only allow you to add additional fields on top of that? Is there a simple way to delete them, or do I basically have to add those fields (and ignore them) to our existing database in order to be able to use django with it?
AbstractUser is for when you want to add additional fields to Django's defaults. Use AbstractBaseUser if you don't want all those fields.
I'm using a CustomUser in my model. This is the User Manager.
class UserManager(BaseUserManager):
def create_user(self, email, username, password=None, is_staff=False, is_superuser=False, is_active=False,
is_bot=False, is_mobile_verified=False, is_online=True, is_logged_in=True):
logger = logging.getLogger(__name__)
logger.info("REGULAR user created!")
if not email:
raise ValueError('Email is required')
if not username:
raise ValueError('Username is required.')
email = self.normalize_email(email)
user = self.model(email=email, username=username, is_staff=is_staff, is_superuser=is_superuser,
is_active=is_active, is_bot=is_bot, is_mobile_verified=is_mobile_verified,
is_online=is_online, is_logged_in=is_logged_in)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
logger = logging.getLogger(__name__)
logger.info("SUPER user created!")
return self.create_user(email, username, password=password, is_staff=True, is_superuser=True, is_active=True,
is_bot=False, is_mobile_verified=False, is_online=True, is_logged_in=True)
This is my definition of the custom user model.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=255)
mobile = PhoneNumberField(null=True)
username = models.CharField(null=False, unique=True, max_length=255)
full_name = models.CharField(max_length=255, blank=True, null=True)
birthday = models.DateField(null=True)
gender = models.CharField(max_length=255, null=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_mobile_verified = models.BooleanField(default=False)
is_online = models.BooleanField(default=False)
is_logged_in = models.BooleanField(default=True)
is_bot = models.BooleanField(default=False)
location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
#objects = UserManager()
get_user_model().objects.create_user(...)
If I uncomment the line objects = UserManager() then I can run the server but the super users created from the admin backend can't log in.
If I use get_user_model() the code breaks and I get the following error
"AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'bouncer.User' that has not been installed
But in my settings.py I've define auth user model
AUTH_USER_MODEL = 'bouncer.User'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
What am I doing wrong here?
For anyone reading this in 2020, I suspect the problem is dependency related. I ran into the same error as OP.
Your first two checks should be:
1 - Is the app in the installed apps list in your settings.py?
2 - Is the AUTH_USER_MODEL = "app_name_from_apps_py.model_name" set in settings.py?
This was as far as most of the other responses I read go.
What I didn't realise on reading the docs is that to use get_user_model() you need to have established your model first. Of course, right?!
So above, where OP is using get_user_model(), they are creating a circular dependency.
You cannot use get_user_model() within the class that creates this model.
That error looks like bouncer isn't in your INSTALLED_APPS.
So to clarify, you have to have
bouncer/models.py that contains the User model (or models/__init__.py which imports the model from another file)
'bouncer' in the INSTALLED_APPS list in the settings
AUTH_USER_MODEL = 'bouncer.User' (as you do).
In my case I accidentally pasted in Meta class of my custom user the attribute abstract = True. So it raises this error.
For me this was because I had myapp/models/__init__.py and I tried putting the custom User model definition in myapp/models/user.py, and setting AUTH_USER_MODEL = myapp.User. Moving the custom User model definition into myapp/models/__init__.py fixed it. I was unable to import it in __init__.py; I had to move the definition there.
Make sure that you register your model in admin.py and not in models.py
# admin.py
from django.contrib.auth.admin import UserAdmin
admin.site.register(YourUser, UserAdmin)
This solved the problem for me.
Never put custom user model and additional models containing links to user model in one models.py file in one app. This can be a matter of that error.
Bad idea leading to that error:
# customUserModel/models.py
from django.contrib.auth.models import AbstractUser
from django.conf import settings
class User(AbstractUser):
pass
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
some_field = models.CharField(max_length=25)
This is a good idea:
# customUserModel/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
# additionalUserProfile/models.py
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
some_field = models.CharField(max_length=25)
#settings.py
AUTH_USER_MODEL = 'customUserModel.User'
Try shifting the position of the bouncer app in your INSTALLED APPS, this worked for me.
For some reason the import from django.contrib.auth.backends import BaseBackend caused this problem. I suspect that BaseBackend tries to use the user model, but can't because it's defined further down the file. However if you define your model BEFORE that import then it'll work just fine.
I'm aware this is an old question but I struggled with this issue for two days before finding my mistake, which was failing to follow the models organization in the Django Models docs.
I would have just commented on #5fec's answer, but I don't have the reputation yet. My answer is similar to his, except importing the model in models/__init__.py worked for me. I had neglected the __init__.py file entirely.
If you have the AUTH_USER_MODEL = <app_name>.<user_model> correctly written, and you have your '<app_name>', in your INSTALLED_APPS list, but you're still getting this error, it's possible that your <custom_user> model (e.g. User) is in the wrong place.
It needs to be defined in either:
<app_name>.models.py
OR
<app_name>/models/<arbitrary_name>.py AND there is an <app_name>/models/__init__.py that contains the line from .<arbitrary_name> import <custom_user>
try importing from django.db import models only after importing from django.contrib.auth.models import AbstractUser