I'm using Django 3.0. I want to add an action to the User ChangeList in the admin.
The documentation for admin actions indicates that to add an action to an admin page, I need to add either the method or a reference to it to the Model's admin.ModelAdmin
subclass in admin.py
:
# admin.py
from django.contrib import admin
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'status']
ordering = ['title']
actions = [make_published]
or
# admin.py
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
...
actions = ['make_published']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
Since the User Model's admin.ModelAdmin
subclass is in the auth
system and not in my admin.py
, though, I don't know where to put the code for this case.
I tried following user Davor Lucic's answer to a much older but similar question
from django.contrib.auth.models import User
class UserAdmin(admin.ModelAdmin):
actions = ['activate_user','deactivate_user']
def activate_user(self, request, queryset):
queryset.update(is_active=True)
def deactivate_user(self, request, queryset):
queryset.update(is_active=False)
activate_user.short_description = "Activate user(s)"
deactivate_user.short_description = "Deactivate user(s)"
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
When I try this, the server halts with the error (full stack trace requested by responder):
Project/project/app/admin.py changed, reloading.
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "Project/env/lib/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "Project/env/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 109, in inner_run
autoreload.raise_last_exception()
File "Project/env/lib/python3.6/site-packages/django/utils/autoreload.py", line 76, in raise_last_exception
raise _exception[1]
File "Project/env/lib/python3.6/site-packages/django/core/management/__init__.py", line 357, in execute
autoreload.check_errors(django.setup)()
File "Project/env/lib/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "Project/env/lib/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "Project/env/lib/python3.6/site-packages/django/apps/registry.py", line 122, in populate
app_config.ready()
File "Project/env/lib/python3.6/site-packages/django/contrib/admin/apps.py", line 24, in ready
self.module.autodiscover()
File "Project/env/lib/python3.6/site-packages/django/contrib/admin/__init__.py", line 26, in autodiscover
autodiscover_modules('admin', register_to=site)
File "Project/env/lib/python3.6/site-packages/django/utils/module_loading.py", line 47, in autodiscover_modules
import_module('%s.%s' % (app_config.name, module_to_search))
File "Project/env/lib/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "Project/project/app/admin.py", line 153, in <module>
admin.site.unregister(User)
File "Project/env/lib/python3.6/site-packages/django/contrib/admin/sites.py", line 144, in unregister
raise NotRegistered('The model %s is not registered' % model.__name__)
django.contrib.admin.sites.NotRegistered: The model User is not registered
I think the error might be with the register()
and unregister()
functions, but these have no documentation so I have no way of knowing.
Is there a simple, functioning way to add an admin action to the auth.models.User
Model?
EDIT: Here's the final result, putting everything together thanks to 'tim-mccurrach':
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
class UserAdmin(AuthUserAdmin):
actions = ['activate_user','deactivate_user']
def activate_user(self, request, queryset):
queryset.update(is_active=True)
def deactivate_user(self, request, queryset):
queryset.update(is_active=False)
activate_user.short_description = "Activate selected users"
deactivate_user.short_description = "Deactivate selected users"
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I checked, and using this scheme it isn't necessary to re-order INSTALLED_APPS
in settings.py
(it's fine if your app comes before contrib.admin
and contrib.auth
).
The problem is the order of your INSTALLED_APPS
.
Because your App
app comes before contrib.auth
, the app.admin.py
file is imported before the conrib.auth.admin.py
file. This means you are trying to unregister the User
admin, before it's even been registered.
change the INSTALLED_APPS
order to put your own app after the contrib.auth
app, and everything should work :)
Note also: Rather than inheriting from admin.ModelAdmin
, if you inherit from auth
s UserAdmin
you'll get all of the existing functionality e.g:
from django.contrib.auth.admin import UserAdmin as OriginalUserAdmin
class UserAdmin(OriginalUserAdmin):
actions = ['activate_user','deactivate_user']
...
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
As it happens, I think importing the UserAdmin
here would also fix your problem without having to change the INSTALLED_APPS
order, since whilst importing UserAdmin
the code will have run to register the old UserAdmin
. Changing the order of the apps seems like the more robust way to fix your issue though.