# -*- coding: utf-8 -*- ''' Tricky admin -> xadmin merger. Alex Moiseenko aka IMDagger. ''' import logging import types from functools import wraps, update_wrapper from django.http import HttpRequest from django.contrib.admin import ModelAdmin class IgnoredAdminModel(Exception): pass def translate(klass, model_obj): ''' Very tricky function to merge Django admin's model descriptors into the XAdmin registry, because it doesn't work from the box due to the huge difference and some copied xadmin's functionality (this one has own incompatible metaclass, incompatible methods with same goal as Django's admin does). This function tries to fill this gap. ''' if issubclass(klass, ModelAdmin): attrs = {} slices = reversed((klass,) + klass.__bases__) # all bases after ModelAdmin is_nested = lambda s: issubclass(s, ModelAdmin) and s != ModelAdmin custom_slices = filter(is_nested, slices) for admin_slice in custom_slices: # copy just only custom methods and fields for k, v in admin_slice.__dict__.iteritems(): if k.startswith('__'): continue if isinstance(v, types.FunctionType): def method_for(k, v): @wraps(v) def inner(self, *args, **kwargs): real_handler = getattr(model_obj, k) if args and isinstance(args[0], HttpRequest): # args list already contains request return real_handler(*args, **kwargs) # request should be first argument after self elif 'request' in real_handler.im_func.func_code.co_varnames[:2]: return real_handler(self.request, *args, **kwargs) else: return real_handler(*args, **kwargs) return inner attrs[k] = method_for(k, v) elif not isinstance(v, property): attrs[k] = v # xadmin doesn't work with func-obj specified columns if 'list_display' in attrs: str_list = [] for idx, column in enumerate(attrs['list_display']): if callable(column): internal_name = '_list_{name}_{unique}'.format(name=column.__name__, unique=idx) column_handler = staticmethod(column) attrs[internal_name] = column_handler # patch __func__, because column_handler # is a static method object yet update_wrapper(column_handler.__func__, column) str_list.append(internal_name) else: str_list.append(column) display_type = type(attrs['list_display']) attrs['list_display'] = display_type(str_list) essential = type('{0}Wrapper'.format(klass.__name__), (object,), attrs) return essential else: raise IgnoredAdminModel('This function doesn\'t support {0} yet'.format(klass)) def sew(admin, to, RegistryError): xadmin = to for model, admin_obj in admin.site._registry.iteritems(): try: x = xadmin.site.register(model, translate(admin_obj.__class__, admin_obj)) admin_obj.admin_site = x except (RegistryError, IgnoredAdminModel), e: logger = logging.getLogger(__name__) logger.debug('Admin sewing notice', exc_info=True, extra={'descriptor': e})