Skip to content

Instantly share code, notes, and snippets.

@gengue
Forked from IMDagger/seamless_admin.py
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gengue/3dd99a57b084377169b6 to your computer and use it in GitHub Desktop.
Save gengue/3dd99a57b084377169b6 to your computer and use it in GitHub Desktop.
# -*- 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})
# how to use merger:
...
from django.contrib import admin
admin.autodiscover()
from xadmin import sites
import xadmin
xadmin.autodiscover()
seamless_admin.sew(admin, to=xadmin, RegistryError=sites.AlreadyRegistered)
urlpatterns = patterns('',
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment