diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index a48381d9063..6cb0c217954 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -1,6 +1,7 @@ from django import http, template from django.contrib.admin import ModelAdmin from django.contrib.auth import authenticate, login +from django.core.exceptions import ImproperlyConfigured from django.db.models.base import ModelBase from django.shortcuts import render_to_response from django.utils.safestring import mark_safe @@ -66,19 +67,33 @@ class AdminSite(object): If a model is already registered, this will raise AlreadyRegistered. """ - do_validate = admin_class and settings.DEBUG - if do_validate: - # don't import the humongous validation code unless required + # Don't import the humongous validation code unless required + if admin_class and settings.DEBUG: from django.contrib.admin.validation import validate - admin_class = admin_class or ModelAdmin - # TODO: Handle options + else: + validate = lambda model, adminclass: None + + if not admin_class: + admin_class = ModelAdmin if isinstance(model_or_iterable, ModelBase): model_or_iterable = [model_or_iterable] for model in model_or_iterable: if model in self._registry: raise AlreadyRegistered('The model %s is already registered' % model.__name__) - if do_validate: - validate(admin_class, model) + + # If we got **options then dynamically construct a subclass of + # admin_class with those **options. + if options: + # For reasons I don't quite understand, without a __module__ + # the created class appears to "live" in the wrong place, + # which causes issues later on. + options['__module__'] = __name__ + admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) + + # Validate (which might be a no-op) + validate(admin_class, model) + + # Instantiate the admin class to save in the registry self._registry[model] = admin_class(model, self) def unregister(self, model_or_iterable): diff --git a/tests/regressiontests/admin_registration/__init__.py b/tests/regressiontests/admin_registration/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regressiontests/admin_registration/models.py b/tests/regressiontests/admin_registration/models.py new file mode 100644 index 00000000000..fdfa3691b88 --- /dev/null +++ b/tests/regressiontests/admin_registration/models.py @@ -0,0 +1,64 @@ +""" +Tests for various ways of registering models with the admin site. +""" + +from django.db import models +from django.contrib import admin + +class Person(models.Model): + name = models.CharField(max_length=200) + +class Place(models.Model): + name = models.CharField(max_length=200) + +__test__ = {'API_TESTS':""" + + +# Bare registration +>>> site = admin.AdminSite() +>>> site.register(Person) +>>> site._registry[Person] + + +# Registration with a ModelAdmin +>>> site = admin.AdminSite() +>>> class NameAdmin(admin.ModelAdmin): +... list_display = ['name'] +... save_on_top = True + +>>> site.register(Person, NameAdmin) +>>> site._registry[Person] + + +# You can't register the same model twice +>>> site.register(Person) +Traceback (most recent call last): + ... +AlreadyRegistered: The model Person is already registered + +# Registration using **options +>>> site = admin.AdminSite() +>>> site.register(Person, search_fields=['name']) +>>> site._registry[Person].search_fields +['name'] + +# With both admin_class and **options the **options override the fields in +# the admin class. +>>> site = admin.AdminSite() +>>> site.register(Person, NameAdmin, search_fields=["name"], list_display=['__str__']) +>>> site._registry[Person].search_fields +['name'] +>>> site._registry[Person].list_display +['__str__'] +>>> site._registry[Person].save_on_top +True + +# You can also register iterables instead of single classes -- a nice shortcut +>>> site = admin.AdminSite() +>>> site.register([Person, Place], search_fields=['name']) +>>> site._registry[Person] + +>>> site._registry[Place] + + +"""}