- Author:
- Mimino
- Posted:
- November 8, 2013
- Language:
- Python
- Version:
- Not specified
- Score:
- 0 (after 0 ratings)
Official GitHub page: https://github.com/Mimino666/django-admin-autoregister
One call to autoregister_admin() automatically creates and registers admin views for all the models in the specified module with intelligent linking between ForeignKey, OneToOneField and ManyToManyField fields.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | from types import ModuleType
from django.contrib import admin
from django.contrib.admin.util import quote
from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.models import ForeignKey, OneToOneField, Count
from django.db.models.base import ModelBase
def _get_admin_change_url(field):
'''Return function to generate admin change view url for a related object.
@param field: field pointing to a related object
@type field: ForeignKey or OneToOneField
'''
related_model = field.related.parent_model
def f(self, obj):
link_args = getattr(obj, field.attname)
if link_args is None:
return u'(None)'
# we could use field.name to output __unicode__() of the related object,
# but that would require to prefetch related objects, which can be slow
link_text = getattr(obj, field.attname)
try:
url = reverse('admin:%s_%s_change' %
(related_model._meta.app_label, related_model._meta.module_name),
args=[quote(link_args)])
except NoReverseMatch:
return link_text
return u'<a href="%s">%s</a>' % (url, link_text)
f.allow_tags = True
f.short_description = field.name
return f
def _get_admin_changelist_url(field):
'''Return function to generate admin changlelist view url for the related
objects.
@param field: field pointing to the related objects
@type field: ManyToManyField
'''
related_model = field.related.parent_model
def f(self, obj):
link_cond = '%s=%s' % (field.related_query_name(), quote(obj.pk))
link_text = u'%s (%s)' % (field.name.title(), getattr(obj, '%s__count' % field.name))
try:
url = reverse('admin:%s_%s_changelist' %
(related_model._meta.app_label, related_model._meta.module_name))
except NoReverseMatch:
return link_text
return u'<a href="%s?%s">%s</a>' % (url, link_cond, link_text)
f.allow_tags = True
f.short_description = field.name
return f
def _get_admin_queryset(admin_class, count_field_names):
'''Return function to generate queryset to efficiently fetch counts()
of related objects.
'''
counts = map(Count, count_field_names)
def queryset(self, request):
qs = super(admin_class, self).queryset(request)
if counts:
qs = qs.annotate(*counts)
return qs
return queryset
def autoregister_admin(module, exclude=None, model_fields=None,
admin_fields=None):
'''
@param module: module containing django.db.models classes
@type module: str or __module__
If you are providing str, use absolute path.
@param exclude: list of classes to exclude from auto-register
@type exclude: iterable of strings or None
@param model_fields: dictionary of additional fields for list_display
{'model name': [field1, field2, ...]}
@type model_fields: dict or None
@param admin_fields: dictionary of additional admin fields
{'model name': {name: value, ...}}
@type admin_fields: dict or None
'''
exclude = exclude or []
model_fields = model_fields or {}
admin_fields = admin_fields or {}
if isinstance(module, basestring):
module = __import__(module, fromlist=[module.split('.')[-1]])
elif not isinstance(module, ModuleType):
raise TypeError('invalid type of argument `module`, expected `str` or '
'`ModuleType`, got %s.' % type(module))
# collect the models to register
models = []
for model in module.__dict__.values():
if (isinstance(model, ModelBase) and
model.__module__ == module.__name__ and
not model._meta.abstract and
model.__name__ not in exclude):
models.append(model)
# for each model prepare an admin class `<model_name>Admin`
for model in models:
admin_class = type('%sAdmin' % model.__name__, (admin.ModelAdmin,), dict())
# list pk as the first value
admin_class.list_display = [model._meta.pk.name]
# list all the other fields
for field in model._meta.fields:
if field == model._meta.pk:
continue
admin_field_name = field.name
# create link for related objects
if isinstance(field, (ForeignKey, OneToOneField)):
admin_field_name += '_link'
setattr(admin_class, admin_field_name, _get_admin_change_url(field))
admin_class.list_display.append(admin_field_name)
count_field_names = []
for field in model._meta.many_to_many:
count_field_names.append(field.name)
admin_field_name = field.name + '_link'
setattr(admin_class, admin_field_name, _get_admin_changelist_url(field))
admin_class.list_display.append(admin_field_name)
# add custom model fields
for name in model_fields.get(model.__name__, []):
admin_class.list_display.append(name)
# prefetch related fields
admin_class.queryset = _get_admin_queryset(admin_class, count_field_names)
# add custom admin fields
for (name, value) in admin_fields.get(model.__name__, {}).iteritems():
setattr(admin_class, name, value)
try:
admin.site.register(model, admin_class)
# pass gracefully on duplicate registration errors
except admin.sites.AlreadyRegistered:
pass
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 9 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 4 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 5 months ago
- Help text hyperlinks by sa2812 1 year, 5 months ago
Comments
Please login first before commenting.