Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use custom AdminSite class?

Which is the best way to implement my own django.contrib.admin.sites.AdminSite?

Actually I get a problem with the registration of INSTALLED_APPS in django.contrib.admin.autodiscover. If I use my custom AdminSite class in urls.py, there were no apps displayed on the admin page.

I fixed this with a litte hack. I wrote this class:

from django.contrib.admin.sites import site as default_site  class AdminSiteRegistryFix( object ):     '''     This fix links the '_registry' property to the orginal AdminSites     '_registry' property. This is necessary, because of the character of     the admins 'autodiscover' function. Otherwise the admin site will say,     that you havn't permission to edit anything.     '''      def _registry_getter(self):         return default_site._registry      def _registry_setter(self,value):         default_site._registry = value      _registry = property(_registry_getter, _registry_setter) 

And implement my custom AdminSite like this:

from wltrweb.hacks.django.admin import AdminSiteRegistryFix from django.contrib.admin import AdminSite  class MyAdminSite( AdminSite, AdminSiteRegistryFix ):     # do some magic     pass           site = MyAdminSite() 

So I can use this site for urls.py.

Anyone knows a better way? Since I access a var starting with a underscore it is no more than a hack. I don't like hacks.

Edit: Another way would be to rewrite the django.contrib.admin.autodiscover function, but in this case I would have redundant code.

like image 548
svenwltr Avatar asked Feb 02 '11 16:02

svenwltr


People also ask

How do I access my Django admin page?

To login to the site, open the /admin URL (e.g. http://127.0.0.1:8000/admin ) and enter your new superuser userid and password credentials (you'll be redirected to the login page, and then back to the /admin URL after you've entered your details).

How do I change the administrator name in Django?

To change the admin site header text, login page, and the HTML title tag of our bookstore's instead, add the following code in urls.py . The site_header changes the Django administration text which appears on the login page and the admin site. The site_title changes the text added to the <title> of every admin page.

Where is Django admin template?

The default templates used by the Django admin are located under the /django/contrib/admin/templates/ directory of your Django installation inside your operating system's or virtual env Python environment (e.g. <virtual_env_directory>/lib/python3. 5/site-packages/django/contrib/admin/templates/ ).


2 Answers

The Problem

Using a custom class derived from django.contrib.admin.AdminSite for the admin site of a project, without having to write custom registration code to register models with the new class. When I use 3rd party apps with their own models, I'd rather not have to edit custom registration code only because models were added or removed from these apps.

The Solution

You have to switch the instance created with the default class used for the admin site to your own instance, created with your own class before django.contrib.admin's autodiscover function is called. I do this by:

  1. Having an app that will perform the switch. (I use my project-specific app named core for my own purposes.)

  2. Two choices:

    1. Django 1.6 to 1.9: use __init__ of the app to perform the switch. In Django 1.8, you will get a deprecation warning due to the change in Django 1.9 quoted below. Note that this method will work with 1.9 too because the Django modules loaded by the code shown below have been changed in 1.9 so that they no longer load models. When I use this method my core/__init__.py file contains:

      from django.contrib import admin from django.contrib.admin import sites  class MyAdminSite(admin.AdminSite):     pass  mysite = MyAdminSite() admin.site = mysite sites.site = mysite 
    2. Django 1.9 and over: use the app configuration of the app to perform the switch. As of Django 1.9, as the release notes state:

      All models need to be defined inside an installed application or declare an explicit app_label. Furthermore, it isn’t possible to import them before their application is loaded. In particular, it isn’t possible to import models inside the root package of an application.

      I prefer to limit the imports I do at the root level to avoid the risk of loading models. While as of version 1.9 using the __init__ method above will work, there's no telling if 1.10 or a later version will introduce a change that will cause problems.

      When I use this method the core/__init__.py sets default_app_config = "core.apps.DefaultAppConfig" and I have a core/apps.py like this:

      from django.apps import AppConfig  class DefaultAppConfig(AppConfig):     name = 'core'      def ready(self):         from django.contrib import admin         from django.contrib.admin import sites          class MyAdminSite(admin.AdminSite):             pass          mysite = MyAdminSite()         admin.site = mysite         sites.site = mysite 

      While it is possible to use this method with versions 1.7 and 1.8, it is a bit risky to use it with those versions. See the notes below.

  3. Placing this app earlier than django.contrib.admin in the INSTALLED_APPS list. (This is absolutely necessary for 1.7 and later. In earlier versions of Django, it might work okay even if the app is later than django.contrib.admin. However, see the notes below.)

Notes and Caveats

  • The app that performs the switch should really be the first app in the INSTALLED_APPS list so as to minimize the chance that something else will grab the value of site from django.contrib.admin before the switch is made. If another app manages to get that value before the switch is done, then that other app will have a reference to the old site. Hilarity will surely ensue.

  • The method above won't work nicely if two apps are trying to install their own new default admin site class. This would have to be handled on a case-by-case basis.

  • It is possible that a future release of Django could break this method.

  • For version prior to 1.9, I preferred using __init__ to do site switch over using the app configuration because the documentation on initialization indicates that the ready() method of the app configurations is called relatively late. Between the time an app's module is loaded, and the time ready() is called, models have been loaded, and in some case, it could mean that a module has grabbed the value of site from django.contrib.admin before ready is called. So as to minimize the risk, I have the app's __init__ code do the switch.

    I believe the risk that existed in version 1.7 and 1.8 and that I avoided by using __init__ to perform the site switch as early as possible does not exist in 1.9. Everybody is prohibited from loading modules before all the applications are loaded. So doing the switch in the ready callback of the first application listed in INSTALLED_APPS should be safe. I've upgraded a large project to 1.9 and used the app configuration method, without any problem.

like image 153
Louis Avatar answered Oct 02 '22 22:10

Louis


From Django 2.1, there is an 'out-of-the-box' solution: https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#overriding-the-default-admin-site

from django.contrib import admin  class MyAdminSite(admin.AdminSite): ... 

Swapping the custom admin site is now done by adding your own AdminConfig to installed apps.

from django.contrib.admin.apps import AdminConfig  class MyAdminConfig(AdminConfig):     default_site = 'myproject.admin.MyAdminSite' 


INSTALLED_APPS = [     ...     'myproject.apps.MyAdminConfig',  # replaces 'django.contrib.admin'     ... ] 

Note the difference between AdminConfig and SimpleAdminConfig, where the latter doesn't trigger admin.autodiscover(). I'm currently using this solution in a project.

like image 21
Elwin Avatar answered Oct 02 '22 21:10

Elwin