Django Problem with count of model objects - python

I need to disable adding new users in the admin panel once the number of users exceeds a particular value. The code below works to remove the "add user" button when I test it with hard coded integers. However, this line does not appear to be returning the count of users without hard coding a count: usercount = CustomUser.objects.count()
Any ideas for getting the count of users already added?
models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import Group
from django.contrib import admin
from django.db.models import Count
from django.db.models.signals import post_save
class CustomUser(AbstractUser):
full_name = models.CharField(max_length=250, null=True)
age = models.PositiveIntegerField(null=True, blank=True)
employee_type = models.ForeignKey(Group, null=True, on_delete=models.SET_NULL, default=1)
is_active = models.BooleanField(null=False, default=True)
# disable add new user in the admin panel
class RemoveAddNew(admin.ModelAdmin):
usercount = CustomUser.objects.count()
if usercount > 5:
def has_add_permission(self, request, obj=None):
return False
admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser, RemoveAddNew
class CustomUserAdmin(RemoveAddNew, UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['username', 'email', 'full_name', 'age', 'is_staff', 'is_active']
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('full_name', 'age', )}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {'fields': ('email','full_name', 'age', 'employee_type', 'is_active')}),
)
admin.site.register(CustomUser,CustomUserAdmin)

I think you over code it. My solution is simpler and shorter:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['username', 'email', 'full_name', 'age', 'is_staff', 'is_active']
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('full_name', 'age', )}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {'fields': ('email','full_name', 'age', 'employee_type', 'is_active')}),
)
def has_add_permission(self, request):
return CustomUser.objects.count() < 6
admin.site.register(CustomUser,CustomUserAdmin)

Related

Admin: Show related inline ManyToMany objects as MultipleChoiceField

I've overridden the UserAdmin class and wanted to add a user profile and some related objects as inline.
Userprofile works as expected, but for the ManyToMany relations I get now a table with related objects. That's not really ideal for my application, it's a bit cumbersome to change the related objects, and there's no need to add new objects this way.
I'd like to have a simple MultipleChoiceField containing the related objects. Is there an easy way to achieve this?
Here's my userprofile/admin.py:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from django_admin_listfilter_dropdown.filters import RelatedOnlyDropdownFilter
from driverslog.models import Branch
from driverslog.models import Car
from .models import Userprofile
User = get_user_model()
class ProfileInline(admin.TabularInline):
model = Userprofile
can_delete = False
max_num = 0
extra = 0
fk_name = 'user'
class CarInline(admin.TabularInline):
model = Car.users.through
can_delete = True
verbose_name = _('Car')
verbose_name_plural = _('Cars')
extra = 0
class BranchInline(admin.TabularInline):
model = Branch.users.through
can_delete = True
verbose_name = _('Branch')
verbose_name_plural = _('Branches')
extra = 0
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, BranchInline, CarInline)
list_display = ('username', 'first_name', 'last_name', 'is_staff', 'is_superuser', 'represents')
list_filter = ('is_active', 'is_staff', 'is_superuser', ('groups', RelatedOnlyDropdownFilter),
('branches', RelatedOnlyDropdownFilter), ('profile__represent', RelatedOnlyDropdownFilter),
('car', RelatedOnlyDropdownFilter))
def represents(self, obj):
return obj.profile.represent.count()
represents.short_description = _('Represents')
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
I solved it now without using inline fields.
What I did is overriding UserChangeForm and adding the fields for the reverse relation. It's working as expected and by now I didn't find any drawbacks (until now...).
admin.py:
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline,)
form = UserForm
...
fieldsets = (
(_('Personal info'), {'fields': ('username', 'first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
(_('Assignments'), {'fields': ('branches', 'cars')}),
)
forms.py:
class UserForm(UserChangeForm):
branches = forms.ModelMultipleChoiceField(queryset=Branch.objects.all())
cars = forms.ModelMultipleChoiceField(queryset=Car.objects.all())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = kwargs.get('instance')
if instance:
self.fields["branches"].initial = instance.branches.all().values_list('id', flat=True)
self.fields["cars"].initial = instance.cars.all().values_list('id', flat=True)
def save(self, commit=True):
if self.is_valid():
self.instance.cars.set(self.cleaned_data.get('cars'))
self.instance.branches.set(self.cleaned_data.get('branches'))
return super().save(commit)
models.py:
class Userprofile(models.Model):
class Meta:
verbose_name = _('Profile')
verbose_name_plural = _('Profiles')
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, unique=True, related_name='profile', verbose_name=_('User'))
represent = models.ManyToManyField(get_user_model(), related_name='represent', blank=True, verbose_name=_('Represents'))
After 2017, filter_horizontal attribute added by django.
class CustomUserAdmin(UserAdmin):
filter_horizontal = ('branches', 'cars')

Django create CustomUser model

Folks,
I'm trying to create my own User model by extending the AbstractBaseUser model provided by Django.
However, I keep on getting the following error when migrating:
ValueError: The field admin.LogEntry.user was declared with a lazy reference to '<app_name>.user', but app '<app_name>' doesn't provide model 'user'.
What I have done so far is the following:
In app/models.py added CustomUser class together with CustomUserManager with relevant fields and all.
In app/admin.py I've added this:
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from <app_name>.models import CustomUser
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
fields = ('email',)
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('email',)
def clean_username(self):
username = self.cleaned_data["username"]
try:
get_user_model().objects.get(username=username)
except get_user_model().DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
class CustomUserAdmin(UserAdmin):
form = CustomUserChangeForm
add_form = CustomUserCreationForm
fieldsets = (
(None, {'fields': [('username', 'password'),]}),
(('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
Apart from that there is this added to seetings.py
AUTH_USER_MODEL = '<app_name>.CustomUser'
Everything that I found so far suggests that the above given code should make this work, but it doesn't. I spent like 4-5 hours already and I still can't get my head around it. Someone please help
Create your User model like so:
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.models import UserManager
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=75, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'underscores characters'),
validators=[
validators.RegexValidator(re.compile('^[\w]+$'),
_('Enter a valid username.'), 'invalid')
])
first_name = models.CharField(_('first name'), max_length=254, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), max_length = 254, unique = True, null = 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 = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['first_name']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.username
Don't forget to add your custom fields!
Use a custom admin like so:
#admin.register(models.User)
class CustomUserAdmin(UserAdmin):
list_display = ('name', 'username', 'is_staff', )
list_filter = ('is_staff', )
search_fields = ('first_name', 'last_name', 'username', )
readonly_fields = ('date_joined', 'last_login', )
fieldsets = (
(None, {
'fields': ('username', 'password')
}),
("Personal Information", {
'fields': ('first_name', 'last_name', 'email')
}),
("Permissions", {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')
}),
("Important Dates", {
'fields': ('last_login', 'date_joined')
}),
)
Then finally add it to your settings:
AUTH_USER_MODEL = '<app_name>.User'
A working demonstration can be found in my GitHub repository.

Remove/hiding username field in django admin edit user form

The frontend user signup form doesn't have username field as I am using email to login.Now I have access to django admin.
When I edit an user I get that username field to edit but I do not want that field to be editable or view-able at all.
admin.py --
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import UserProfile
class ProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'Profile'
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, )
list_display = ('email', 'first_name', 'last_name', 'is_staff')
list_select_related = ('profile', )
exclude = ('username',)
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(CustomUserAdmin, self).get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
forms.py --
from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Field
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteField
from phonenumber_field.formfields import PhoneNumberField
from . import models
from captcha.fields import ReCaptchaField
class SignUpForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
phone_number = PhoneNumberField(label=_("Phone (Please state your country code eg. +44)"))
organisation = forms.CharField(max_length=50)
email = forms.EmailField()
password1 = forms.CharField(max_length=20)
password2 = forms.CharField(max_length=20)
captcha = ReCaptchaField(attrs={'theme' : 'clean'})
models.py--
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from easy_thumbnails.fields import ThumbnailerImageField
from ciasroot.settings import THUMBNAILER_SIZES, UPLOAD_PATH
from ciasroot.constants import GENDERS, LANGUAGES
from ciasroot.util import HashedPk
from phonenumber_field.modelfields import PhoneNumberField
import math, decimal, datetime, os
class UserProfile(models.Model, HashedPk):
user = models.OneToOneField(User, unique=True, related_name ='profile')
job_title = models.CharField(max_length=128, blank=True, null=False, default="")
website = models.URLField(max_length=255, blank=True, null=True)
organisation = models.CharField(max_length=50, blank=True, null=True, default="")
phone_number = PhoneNumberField( blank=True, null=True)
The django version is 1.10 so I used exclude. But it throws an error--
KeyError: "Key 'username' not found in 'UserForm'. Choices are: date_joined, email, first_name, groups, is_active, is_staff, is_superuser, last_login, last_name, password, user_permissions."
Do I need to override the signup form because when adding a new user from admin I don't get the fields as frontend signup form(At some point I need to do that as well).
Any help is highly appreciated.
A discussion about this problem is here.
Briefly:
That's because the stock UserAdmin defines fieldsets in which 'username'
is a field, you need to modify the fieldsets, too.
For example:
class CustomUserAdmin(UserAdmin):
# ...
exclude = ('username',)
fieldsets = (
('Personal info', {'fields': ('full_name', 'email', 'password')}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
)

No “Users” link in “Auth” section of Django admin site

I’ve created a custom user object in my Django app, but don’t have control over user permissions. I believe this is because the Users link isn’t appearing in the Auth section of the Django admin site, where permissions are usually controlled.
Why would it not be showing up?
This is from my models.py file:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, username, password=None):
"""
Creates and saves a user with the given username.
"""
user = self.model()
user.username = username
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, password):
"""
Creates and saves a superuser with the given username.
"""
user = self.create_user(username, password=password)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class FooUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=40, unique=True, db_index=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
my_time_field = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'username'
objects = UserManager()
class Meta:
app_label = 'foo'
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return self.is_admin
In other apps I extend the user model further as needed:
class CocoUser(FooUser):
mobile_number = models.CharField(max_length=64, blank=True, null=True)
first_name = models.CharField(max_length=128, blank=True, null=True)
last_name = models.CharField(max_length=128, blank=True, null=True)
email = models.CharField(max_length=128, blank=True, null=True)
This is from my settings.py file:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'kohlab.force_logout.ForceLogoutMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.humanize',
'django.contrib.messages',
'django_cleanup',
'south',
'myapp',
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"django.core.context_processors.static",
"kohlab.context_processors.site",
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
AUTH_USER_MODEL = ‘myapp.FooUser’
This is from my urls.py file:
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
)
This is from my admin.py file:
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from coco.models import CocoUser
class CocoUserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required fields, plus a repeated password."""
class Meta:
model = CocoUser
fields = ('mobile_number', 'email', 'first_name', 'last_name',)
class CocoUserChangeForm(forms.ModelForm):
"""
A form for updating users. Includes all the fields on the user, but replaces the password field with the initial one.
"""
class Meta:
model = CocoUser
fields = ['is_admin', 'is_staff', 'mobile_number', 'first_name', 'last_name', 'email']
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 CocoUserAdmin(UserAdmin):
# The forms to add and change user instances
form = CocoUserChangeForm
add_form = CocoUserCreationForm
# The fields to be used in displaying the CocoUser model.
# These override the definitions on the base UserAdmin that reference specific fields on auth.User.
list_display = ('id', 'first_name', 'last_name', 'email', 'mobile_number', 'is_admin', 'is_staff',)
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('is_admin', 'is_staff', 'mobile_number', 'first_name', 'last_name', 'email',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('mobile_number', 'email', 'first_name', 'last_name',)}
),
)
search_fields = ('id', 'mobile_number', 'email', 'first_name', 'last_name',)
ordering = ('last_name', 'first_name',)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(CocoUser, CocoUserAdmin)
In the end, the solution was rather simple. I had to adjust my CocoUserAdmin’s fieldsets to expose the permissions.
With a custom class like that, there will be no Users link in the Auth section, because the custom class takes over -- including permissions. These settings won’t be evident though, unless 'groups' and 'user_permissions' are added to fieldsets.
That CocoUserAdmin fieldsets fix is the key. Along the way, I converted FooUser to be a subclass of AbstractUser. This might have been unnecessary; the permissions may well have been there when CocoUser was a subclass of AbstractBaseUser too, but I’m not sure.
From my final models.py file:
from django.contrib.auth.models import AbstractUser
from django.db import models
class FooUser(AbstractUser):
my_time_field = models.DateTimeField(null=True, blank=True)
class CocoUser(FooUser):
mobile_number = models.CharField(max_length=64, blank=True, null=True)
From my final admin.py file:
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from coco.models import CocoUser
class CocoUserCreationForm(forms.ModelForm):
"""A form for creating new users."""
class Meta:
model = CocoUser
fields = ('username', 'mobile_number', 'email', 'first_name', 'last_name',)
class CocoUserChangeForm(forms.ModelForm):
"""
A form for updating users. Includes all the fields on the user.
"""
class Meta:
model = CocoUser
fields = ['username', 'password', 'first_name', 'last_name', 'email', 'is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions', 'last_login', 'date_joined',
'my_time_field', 'mobile_number',]
class CocoUserAdmin(UserAdmin):
# The forms to add and change user instances
form = CocoUserChangeForm
add_form = CocoUserCreationForm
# The fields to be used in displaying the CocoUser model.
# These override the definitions on the base UserAdmin that reference specific fields on auth.User.
list_display = ('id', 'first_name', 'last_name', 'email', 'mobile_number',)
fieldsets = (
(None, {'fields': ('username', 'password',)}),
('Personal info', {'fields': ('first_name', 'last_name', 'email', 'date_joined', 'last_login', 'is_online',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions',)}),
('Coco', {'fields': ('my_time_field', 'mobile_number',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'mobile_number', 'email', 'first_name', 'last_name',)}
),
)
search_fields = ('id', 'mobile_number', 'email', 'first_name', 'last_name',)
ordering = ('last_name', 'first_name',)
class Meta:
model = CocoUser
I derived from AbstractUser and had the same problem. No Users in Auth section.
That because I forgot to register my custom user admin class.
Maybe you need to register your FooUser admin class first.

Adding extra fields to UserAdmin

i have this custom class
class CustomUserAdmin(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2', 'location')}
),
)
fieldsets = (
(None, {'fields': ('username', 'password')}),
(('Personal info'), {'fields': ('first_name', 'last_name', 'email', 'location')}),
(('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'user_permissions')}),
(('Important dates'), {'fields': ('last_login', 'date_joined')}),
(('Groups'), {'fields': ('groups',)}),
)
#UserAdmin.list_display += ('location',)
add_form = MyUserCreationForm
form = MyUserChangeForm
It works fine, untill i uncomment this line
UserAdmin.list_display += ('location',)
then it gives me this error:
CustomUserAdmin.list_display[5], 'location' is not a callable or an attribute of 'CustomUserAdmin' or found in the model 'User'.
any help?
[Edit]
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
location = models.CharField(max_length=30)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
You are not going to modify UserAdmin right?
Supposing that location is an actual field of CustomUser, try to use
list_display = UserAdmin.list_display + ('location',)
EDIT: simpler answer
Use the standard django way to show custom things in the list_display:
class CustomUserAdmin(UserAdmin):
# other things
def user_location(self, u):
try:
return u.get_profile().location
except:
return ''
user_location.short_description = 'Location'
list_display = UserAdmin.list_display + ('user_location',)
EDIT: more info
Anyway, if you are extending the UserForm just for the sake of adding the profile fields, you should look into this link: http://www.thenestedfloat.com/articles/displaying-custom-user-profile-fields-in-djangos-admin/index.html to take advantages of inlines and avoid recreating the whole Form from scratch.

Categories