Ticket #6776: 6776_modeladmin_fix.2.diff

File 6776_modeladmin_fix.2.diff, 4.9 KB (added by brosner, 7 years ago)

slightly better patch and tests

  • new file django/contrib/admin/loading.py

    diff --git a/django/contrib/admin/loading.py b/django/contrib/admin/loading.py
    new file mode 100644
    index 0000000..4481a13
    - +  
     1
     2class ModelAdminCache(object):
     3    """
     4    A cache of ModelAdmin classes. This ensures that once a ModelAdmin is
     5    declared that it will remain the sole class. Basically providing a
     6    guranteed ModelAdmin idenity.
     7    """
     8    def __init__(self):
     9        self.modeladmin_store = {}
     10   
     11    def get(self, key):
     12        return self.modeladmin_store[key]
     13   
     14    def register(self, *admin_classes):
     15        for admin_class in admin_classes:
     16            key = ".".join(admin_class.__module__.split(".")[-2:] + [admin_class.__name__])
     17            if key in self.modeladmin_store:
     18                continue
     19            self.modeladmin_store[key] = admin_class
     20
     21admin_class_cache = ModelAdminCache()
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index 29ce10a..228c529 100644
    a b from django.newforms.formsets import all_valid 
    44from django.newforms.models import _modelform_factory, _inlineformset_factory
    55from django.contrib.contenttypes.models import ContentType
    66from django.contrib.admin import widgets
     7from django.contrib.admin.loading import admin_class_cache
    78from django.contrib.admin.util import get_deleted_objects
    89from django.core.exceptions import ImproperlyConfigured, PermissionDenied
    910from django.db import models, transaction
    class AdminField(object): 
    124125        attrs = classes and {'class': u' '.join(classes)} or {}
    125126        return self.field.label_tag(contents=contents, attrs=attrs)
    126127
     128class ModelAdminMetaclass(type):
     129    def __new__(cls, name, bases, attrs):
     130        new_class = forms.MediaDefiningClass.__new__(cls, name, bases, attrs)
     131        try:
     132            parents = [b for b in bases if issubclass(b, ModelAdmin)]
     133            if not parents:
     134                return new_class
     135        except NameError:
     136            return new_class
     137        # put this in some sort of registry
     138        admin_class_cache.register(new_class)
     139        # due to the way imports happening only use the admin class that is
     140        # already registered.
     141        return admin_class_cache.get(".".join(new_class.__module__.split(".")[-2:] + [new_class.__name__]))
     142
    127143class BaseModelAdmin(object):
    128144    """Functionality common to both ModelAdmin and InlineAdmin."""
    129145    raw_id_fields = ()
    class BaseModelAdmin(object): 
    200216
    201217class ModelAdmin(BaseModelAdmin):
    202218    "Encapsulates all admin options and functionality for a given model."
    203     __metaclass__ = forms.MediaDefiningClass
     219    __metaclass__ = ModelAdminMetaclass
    204220
    205221    list_display = ('__str__',)
    206222    list_display_links = ()
  • django/contrib/admin/sites.py

    diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
    index 6c6f47e..b496fb7 100644
    a b class AdminSite(object): 
    7676        if isinstance(model_or_iterable, ModelBase):
    7777            model_or_iterable = [model_or_iterable]
    7878        for model in model_or_iterable:
    79             if model in self._registry:
    80                 raise AlreadyRegistered('The model %s is already registered' % model.__name__)
     79            if model in self._registry and self._registry[model].__class__ is not admin_class:
     80                raise AlreadyRegistered('The model %s is already registered to %s' % (model.__name__, self._registry[model].__class__.__name__))
    8181            self._registry[model] = admin_class(model, self)
    8282
    8383    def unregister(self, model_or_iterable):
  • tests/regressiontests/modeladmin/models.py

    diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py
    index 5e67c87..0a04691 100644
    a b we'll just pass in None. 
    1717
    1818>>> request = None
    1919
     20Ensure that only the first ModelAdmin ever declared is the only one for the
     21app.
     22
     23>>> class OneBandAdmin(ModelAdmin):
     24...     pass
     25
     26>>> class TwoBandAdmin(ModelAdmin):
     27...     pass
     28
     29>>> one_band_admin_id = id(OneBandAdmin)
     30>>> id(OneBandAdmin) != id(TwoBandAdmin)
     31True
     32
     33>>> class OneBandAdmin(ModelAdmin):
     34...     pass
     35
     36>>> one_band_admin_id == id(OneBandAdmin)
     37True
     38
    2039>>> band = Band(name='The Doors', bio='')
    2140
    2241Under the covers, the admin system will initialize ModelAdmin with a Model
    properly. This won't, however, break any of the admin widgets or media. 
    96115...     class Meta:
    97116...         model = Band
    98117
    99 >>> class BandAdmin(ModelAdmin):
     118>>> class AnotherBandAdmin(ModelAdmin):
    100119...     form = AdminBandForm
    101120
    102 >>> ma = BandAdmin(Band, site)
     121>>> ma = AnotherBandAdmin(Band, site)
    103122>>> ma.get_form(request).base_fields.keys()
    104123['name', 'bio', 'sign_date', 'delete']
    105124>>> type(ma.get_form(request).base_fields['sign_date'].widget)
Back to Top