My model is relatively simple, but I want users to be member of a club. Superusers are not members of a club.
I decided to use a Custom User Model Extending AbstractBaseUser and created the models, managers and everything works fine. Now I want to extend the model by a property.
models.py:
from .managers import MyUserManager
class K2User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
is_staff = models.BooleanField(_('active'), default=True)
# club_name = models.ForeignKey(Clubs, null=True,
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_club_name(self):
return self.club_name
def __str__(self):
return self.email
class Clubs(models.Model):
club_name = models.CharField(max_length=32, unique=True)
club_create_date = models.DateTimeField('date created')
club_address = models.CharField(max_length=200)
email = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, verbose_name=_('user'), on_delete=models.CASCADE)
managers.py
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class MyUserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
# club_name = club_name
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', False)
extra_fields.setdefault('is_staff', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_staff', True)
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
return self._create_user(email, password, **extra_fields)
how can I add the club name to a normal user, so that he has to be member of a club, but the superuser does not have this property (or just set it null)?
** Edit **
in my admin view I still only see one member for each club - I guess I have to tinker with the admin.py for that?
admin.py:
class K2UserAdmin(UserAdmin):
add_form = K2UserCreationForm
form = K2UserChangeForm
model = K2User
list_display = ('email', 'date_joined', 'is_active',)# 'merchant',)
list_filter = ('email', 'date_joined', 'is_active',)# 'merchant',)
fieldsets = ( (None, { 'fields': ('email', 'password') } ),
('Permissions', { 'fields': ('date_joined', 'is_active') } ), )
add_fieldsets = ( (None, {'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'date_joined', 'is_active') } ), )
readonly_fields = ('date_joined',)
admin.site.register(K2User, K2UserAdmin)
admin.site.register(Clubs)
The best way to implement this is as follows:
First of all, you need to create one model between Club and User
(rather than create M2M field). I named it Enrollment.
Put Club and User foreign key this model.
So:
class Enrollment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name=_('enrollment_users'), on_delete=models.CASCADE)
club = models.ForeignKey(Club, related_name=_('enrollment_clubs'), on_delete=models.CASCADE)
If you follow this way, you can do anything you want in this relation in the future.
Hope it help.
Related
I have a custom User model in my models.py and custom UserManager as well. My custom User model has username field with changed name to login and same is updated in UserManager as well. Everything goes fine, but when I try to create superuser using command python manage.py createsuperuser, it asks for login but it does not ask for email which at the end gives me this error:
TypeError: UserManager.create_superuser() missing 1 required positional argument: 'email'
My custom User model is:
class User(AbstractBaseUser, PermissionsMixin):
login = models.CharField(max_length=254, unique=True)
email = models.EmailField(max_length=254, unique=True)
first_name = models.CharField(max_length=254, null=True, blank=True)
last_name = models.CharField(max_length=254, null=True, blank=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'login'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
My custom UserManager is:
class UserManager(BaseUserManager):
def _create_user(self, login, email, password, is_staff, is_admin, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
now = timezone.now()
email = self.normalize_email(email)
user = self.model(
login=login,
email=email,
is_staff=is_staff,
is_active=True,
is_admin=is_admin,
last_login=now,
date_joined=now,
**extra_fields
)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, login, email, password, **extra_fields):
user = self._create_user(login, email, password, False, False, **extra_fields)
user.save(using=self._db)
return user
def create_superuser(self, login, email, password, **extra_fields):
user=self._create_user(login, email, password, True, True, **extra_fields)
return user
and in admin.py I have:
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('login', 'email', 'password', 'first_name', 'last_name', 'last_login')}),
('Permissions', {'fields': (
'is_active',
'is_staff',
'is_admin',
'groups',
'user_permissions',
)}),
)
add_fieldsets = (
(
None,
{
'classes': ('wide',),
'fields': ('login', 'email', 'password1', 'password2')
}
),
)
list_display = ('login', 'email', 'first_name', 'last_name', 'is_admin', 'last_login')
list_filter = ('is_staff', 'is_admin', 'is_active', 'groups')
search_fields = ('email', 'login',)
ordering = ('email', 'login',)
filter_horizontal = ('groups', 'user_permissions',)
I just had to add 'email' in the REQUIRED_FIELDS variable in my User model definition. i.e.
I had to change following line:
REQUIRED_FIELDS = []
to
REQUIRED_FIELDS = ['email']
I am creating a custom user class for my data base but I can't get my custom fields to show in the admin portal when adding a new user or changing an existing user. I have registered the app in my settings, migrated the data base, and set the AUTH_USER_MODEL in my settings.py. Thank you!
My admin portal looks like this:
And my files look like this:
models.py
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
import secrets
from api_keys.managers import *
class User(AbstractBaseUser, PermissionsMixin):
FULL = 4
MAJOR = 3
MINOR = 2
CONTROLLED = 1
access_groups = [(FULL, 4), (MAJOR, 3), (MINOR, 2), (CONTROLLED, 1)]
username = models.CharField(_('user name'), max_length=50, unique=True)
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=50, blank=True)
last_name = models.CharField(_('last name'), max_length=50, blank=True)
account_name = models.TextField(_('account'), max_length=500, blank=True)
purpose = models.TextField(_('purpose'), max_length=5000, blank=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
is_staff = models.BooleanField(_('staff'), default=False)
access_group = models.IntegerField(_('permission group'), choices=access_groups, default=CONTROLLED)
api_key = models.CharField(_('api key'), max_length=100)
delete_on = models.DateField(_('deletion date'), blank=True, null=True)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'access_group']
def save(self, *args, **kwargs):
if not self.api_key:
self.api_key = secrets.token_urlsafe(16)
super(User, self).save(*args, **kwargs)
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def has_access(self, level):
return self.access_group >= level
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
managers.py
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, email, password, **extra_fields):
if not username:
raise ValueError('The given user name must be set')
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(username=username, email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, username, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', False)
extra_fields.setdefault('is_staff', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_staff', True)
if not extra_fields.get('is_superuser'):
raise ValueError('Superuser must have is_superuser = True')
return self._create_user(username, email, password, **extra_fields)
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from api_keys.forms import UserChangeForm, UserCreationForm
from api_keys.models import User
# Register your models here.
class NewUserAdmin(UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
model = User
list_display = ('username', 'email', 'access_group', 'delete_on', 'api_key')
list_filter = ('username', 'email', 'access_group', 'is_superuser', 'delete_on')
readonly_fields = ['date_joined', 'api_key', 'last_login']
field_sets = (
('User', {'fields': ('username', 'email', 'password', 'access_group')}),
('Permissions', {'fields': (('is_staff', 'is_active'), 'access_group', 'delete_on')}),
)
add_field_sets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', ('password1', 'password2'))
}),
('Permissions', {'fields': (('is_staff', 'is_active'), 'access_group', 'delete_on')})
)
search_fields = ('username', 'email', 'first_name', 'last_name', 'account_name', 'access_group', 'delete_on')
ordering = ('username', 'email', 'first_name', 'last_name', 'access_group', 'account_name', 'delete_on')
admin.site.register(User, NewUserAdmin)
forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from api_keys.models import User
class UserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = User
fields = '__all__'
class UserChangeForm(UserChangeForm):
class Meta(UserChangeForm):
model = User
fields = '__all__'
The layout is controlled by ModelAdmin.fieldsets, not field_sets
class NewUserAdmin(UserAdmin):
...
fieldsets = (
('User', {'fields': ('username', 'email', 'password', 'access_group')}),
('Permissions', {'fields': (('is_staff', 'is_active'), 'access_group', 'delete_on')}),
)
I'm making a custom user model in django, but somehow any new users created through the creation form is missing their assigned permissions (is_active, is_staff, is_admin), preventing them from logging in at all.
Can anyone show me where did I go wrong? Any suggestion is greatly appreciated!
Code snippets:
models.py
class MyUserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, ID, name, email, privilege, password=None):
if not ID:
raise ValueError('Users must have a ID number')
user = self.model(
ID=ID,
name=name,
email=self.normalize_email(email),
privilege=privilege,
)
if self.privilege == 'Admin':
self.is_superuser = True
else:
user.is_active = True
user.is_staff = False
user.is_admin = False
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, NIP, name, email, privilege, password):
new_user = self.create_user(
ID,
name,
email,
privilege,
password=password,
)
new_user.is_superuser = True
new_user.save(using=self._db)
return new_user
class employee(AbstractBaseUser, PermissionsMixin):
ID = models.IntegerField(verbose_name="ID", unique=True)
name = models.CharField(verbose_name="Name", max_length=50, blank=True, unique=True)
email = models.EmailField(verbose_name="email", max_length=225, default='this#mail.com')
Department = models.CharField(max_length=50, choices=DEP_CHOICES, default='')
Role = models.CharField(max_length=50, choices=ROLE_CHOICES, default='')
privilege = models.CharField(max_length=10, default='User')
is_admin = models.BooleanField()
is_staff = models.BooleanField()
is_active = models.BooleanField()
forms.py
class CustomUserCreationForm(UserCreationForm):
ID = forms.CharField(max_length=50)
password = forms.CharField(widget=forms.PasswordInput)
class Meta(UserCreationForm):
model = employee
fields = "__all__"
class CustomUserChangeForm(UserChangeForm):
ID = forms.CharField(max_length=50)
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = employee
fields = "__all__"
admin.py
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
add_form = CustomUserChangeForm
form = CustomUserChangeForm
model = employee
list_display = ('ID', 'name', 'email', 'privilege')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('ID', 'password')}),
('Personal info', {'fields': ('name', 'email', 'privilege')}),
('Permissions', {'fields': ('is_admin', 'is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('ID', 'name', 'email')}
),
)
search_fields = ('name',)
ordering = ('ID',)
filter_horizontal = ()
views.py
def employee_list(request):
data_list = Employee.objects.order_by('NIP')
registered = False
if request.method == "POST":
if 'add_user' in request.POST:
user_form = CustomUserCreationForm(request.POST, prefix='add')
if user_form.is_valid():
new_user = user_form.save()
new_user.set_password(user.password)
new_user.save()
registered = True
return HttpResponseRedirect(reverse('employee'))
else:
user_form = CustomUserCreationForm(prefix='add')
return render(request, 'employee_list.html',
{'SignUpForm': user_form,
'registered': registered,
'Emp_list':data_list})
Instead of using 'priviledge' as a character field in the Employee model, which is very prone to error in user input, use a choice field instead.
class MyUserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, name, email, privilege='member', is_admin=False, is_staff=False, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
name=name,
email=self.normalize_email(email),
privilege=privilege,
)
user.set_password(password)
user.is_active = True
user.is_staff = is_staff
user.is_admin = is_admin
user.save(using=self._db)
return user
def create_superuser(self, name, email, password):
new_user = self.create_user(
name,
email,
privilege='admin',
password=password,
is_staff=True,
is_admin=True
)
return new_user
class Employee(AbstractBaseUser):
PRIVILEGE_LEVEL = (
('Administrator', 'admin'),
('Staff', 'staff'),
('Member', 'member'),
)
# ID = models.IntegerField(verbose_name="ID", unique=True)
id = models.AutoField(primary_key=True)
name = models.CharField(verbose_name="Name", max_length=50, blank=True, unique=True)
email = models.EmailField(verbose_name="email", max_length=225, default='this#mail.com')
Department = models.CharField(max_length=50, choices=DEP_CHOICES, default='')
Role = models.CharField(max_length=50, choices=ROLE_CHOICES, default='')
# privilege = models.CharField(max_length=10, default='User')
privilege = models.CharField(max_length=120, choices=PRIVILEGE_LEVEL, default='member')
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
Also, 'id' is auto generated by django as the primary key field, so it doesnt have to be in the frontend form.
By default the django model form calls MyUserManager.create_user.
and based on the modifications i made above, it defaults to creating a user
without permissions. Unless is_admin and is_staff are implicitly set to True
To create a superuser from your current form, you can update MyUserManager.create_user like so,
def create_user(self, name, email, privilege='member', is_admin=False, is_staff=False, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
name=name,
email=self.normalize_email(email),
privilege=privilege,
)
user.set_password(password)
user.is_active = True
user.is_staff = is_staff
user.is_admin = is_admin
if privilege == 'admin':
user.is_superuser = True
user.save(using=self._db)
return user
But i would advise you update your CustomUserCreationForm instead as such,
class CustomUserCreationForm(UserCreationForm):
# ID = forms.CharField(max_length=50)
password = forms.CharField(widget=forms.PasswordInput)
class Meta(UserCreationForm):
model = Employee
fields = "__all__"
# ADD THIS
def clean(self, value):
print(self.data)
print(self.errors)
cleaned_data = self.cleaned_data
print(cleaned_data)
return cleaned_data
# ADD THIS
def save(self, *args, **kwargs):
data = self.cleaned_data
# email, password1 = v['email'], v['password1']
employee = None
if data['privilege'] == 'admin':
employee = Employee.objects.create_superuser(
name=data['name'],
email=data['email'],
password=data['password'],
)
else:
employee = Employee.objects.create_user(
name=data['name'],
email=data['email'],
password=data['password'],
)
# User is already saved after calling BaseUserManager function.
return employee
MyUserManager.create_superuser calls the MyUserManager.create_user and updates it based on parameter passed to it.
Let me know if you need further clarifications. Cheers.
I have the following custom user model implementation in my Django application :
users/models.py
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email: str,
password: str, is_staff: bool,
is_superuser: bool, **extra_fields):
"""Creates and saves a User with the given email and password.
"""
email = self.normalize_email(email)
user = self.model(email=email, is_staff=is_staff, is_active=True,
is_superuser=is_superuser, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email: str, password=None, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields)
def create_superuser(self, email: str, password: str, **extra_fields):
return self._create_user(email, password, True, True, **extra_fields)
class User(AbstractUser, UUIDModel):
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
email = models.EmailField(unique=True, db_index=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
objects = UserManager()
class Meta:
verbose_name = 'user'
verbose_name_plural = 'users'
ordering = ('-date_joined', )
def get_full_name(self):
return "{} {}".format(self.first_name, self.last_name)
def get_short_name(self):
return self.first_name
def get_email(self):
return self.email
def __str__(self):
return "{}".format(self.email)
And my change form in admin.py looks like this :
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.forms import UserChangeForm as DjangoUserChangeForm
from django.contrib.auth.forms import UserCreationForm as DjangoUserCreationForm
from .models import User
# Forms
# ----------------------------------------------------------------------------
class MyUserCreationForm(DjangoUserCreationForm):
class Meta:
model = User
fields = ("email",)
class MyUserChangeForm(DjangoUserChangeForm):
class Meta:
model = User
fields = '__all__'
# ModelAdmins
# ----------------------------------------------------------------------------
#admin.register(User)
class UserAdmin(AuthUserAdmin):
add_form_template = 'admin/auth/user/add_form.html'
model = User
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
}),
)
readonly_fields = ('date_joined', 'last_login')
form = MyUserChangeForm
add_form = MyUserCreationForm
list_display = ('email', 'first_name', 'last_name', 'is_active')
list_filter = ('is_superuser', 'is_active')
search_fields = ('first_name', 'last_name', 'email')
ordering = ('email',)
I have added these two lines in my settings.py files.
AUTH_USER_MODEL = 'users.User'
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",)
I have extended this model to a custom profile model. But when I try to create another user I get the following error django.db.utils.IntegrityError: (1062, "Duplicate entry '' for key 'username'") . Can anybody help me with this?
I am using Django : Django==2.2 with MySql.
Set
USERNAME_FIELD = 'username'
and test your code.
If everything works then change it.
Add username field in your custom user modal
Because error clearly says duplicate entry for key username which means, you have overrides the default user authentication by email id, so every time you create a user blank username tries to be created. Still, Django won't allow it's default implementation to be unique=True, so you have to override the username attribute also.
class User(AbstractUser):
email = models.EmailField(max_length=100, unique=True)
# add the below one line code
username = models.CharField(max_length=100, unique=False, null=True, default=None)
is_deleted = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
I am have created a custom user model to replace username with email. But when I am trying to add user from Django Admin I get error:
Please correct the errors below.
There is no other information with that error message.
The code for custom user:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name="email_address",
max_length=255,
unique=True,
)
name = models.CharField(max_length=255)
is_staff = models.BooleanField(
default=False,
)
is_active = models.BooleanField(
default=True,
)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
return self.name
def __str__(self):
return self.email
Custom User Manager Code:
class UserManager(BaseUserManager):
def _create_user(self, email, password, **kwargs):
if not email:
raise ValueError("Email is required")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_user(self, email, password=None, **extra_fields):
"""Create and save a regular User with the given email and password."""
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **kwargs):
kwargs.setdefault('is_staff', True)
kwargs.setdefault('is_superuser', True)
kwargs.setdefault('is_active', True)
if kwargs.get('is_staff') is not True:
raise ValueError("Superuser must have is_staff True")
if kwargs.get('is_superuser') is not True:
raise ValueError("Superuser must have is_superuser True")
return self._create_user(email, password, **kwargs)
and the admin code for same:
class CustomUserAdmin(BaseUserAdmin):
add_form = CustomUserCreationForm
form = CustomUserCreationForm
list_display = ('id', 'email', 'is_superuser')
list_filter = ('is_superuser',)
fieldsets = (
(None, {'fields': ('name', 'email', 'password')}),
('Permissions', {'fields': ('is_superuser', 'is_staff')}),
)
add_fieldsets = fieldsets
search_fields = ('name', 'email')
ordering = ('name', 'email')
filter_horizontal = ()
Custom User Form:
# users/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('name', 'email')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = User
fields = ('name', 'email')
I have tried many changes but couldn't figure out the issue. I would appreciate any help on this.
Change:
class Meta:
model = User
fields = ('name', 'email')
To:
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('name', 'email')