Skip to content

Instantly share code, notes, and snippets.

@TomasTomecek
Last active March 12, 2021 21:04
Show Gist options
  • Save TomasTomecek/5191473 to your computer and use it in GitHub Desktop.
Save TomasTomecek/5191473 to your computer and use it in GitHub Desktop.
Automatically add all fields to django admin with links between models
# -*- coding: utf-8 -*-
"""
admin.py
Auto-register admin classes with fields and links to linked model classes
based on http://djangosnippets.org/snippets/997/
"""
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.contrib import admin
from django.db import models as dmodels
from django.db.models import Field, ForeignKey, OneToOneField
from types import ModuleType
def add_link_field(admin_class, field):
field_name = field.name + '_link'
def link(self, instance):
app_name = field.related.parent_model._meta.app_label
reverse_path = "admin:%s_%s_change" % (
app_name,
field.related.parent_model._meta.module_name
)
related_instance = getattr(instance, field.name)
if related_instance:
url = reverse(reverse_path, args=(related_instance.id,))
return mark_safe("<a href='%s'>%s</a>" % (
url,
unicode(related_instance))
)
else:
return unicode(related_instance)
link.allow_tags = True
link.short_description = field.name + ' link'
setattr(admin_class, field_name, link)
admin_class.readonly_fields = list(
getattr(admin_class, 'readonly_fields', [])) + [field_name]
return admin_class, field_name
def register_admin_module(module, exclude=None, new_fields=None):
"""
@param module: module containing django.db.models classes
@type module: str or __module__
@param exclude: list of classes to exclude from auto-register
@type exclude: iterable of str or None
@param new_fields: dictionary of additional fields:
{'model name': ('field_name', callable)}
@type new_fields: dict or None
If you are providing str, use absolute path
"""
exclude = exclude or []
new_fields = new_fields or {}
if isinstance(module, ModuleType):
models = module
elif isinstance(module, basestring):
# import module dynamically -- import leaf, not root
models = __import__(module, fromlist=[module.split('.')[-1]])
else:
raise TypeError("invalid type of argument 'module', expected 'str' or \
'ModuleType', got %s." % type(module))
#get models from current app
mods = []
for x in models.__dict__.values():
if issubclass(type(x), dmodels.base.ModelBase) and \
getattr(x, '__name__', '') not in exclude:
mods.append(x)
admins = []
#for each model prepare an admin class (Admin<model_name>, model)
for c in mods:
admins.append(("%sAdmin" % c.__name__, c))
#create the admin class and register it
for (ac, c) in admins:
admin_class = type(ac, (admin.ModelAdmin,), dict())
admin_class.list_display = []
for field in c._meta.fields:
field_name = field.name
# create link for relations
if issubclass(type(field), (ForeignKey, OneToOneField)):
admin_class, field_name = add_link_field(admin_class, field)
if issubclass(type(field), (ForeignKey, OneToOneField, Field)):
admin_class.list_display.append(field_name)
# add user defined custom fields
for new_field in new_fields.iterkeys():
if c.__name__ == new_field:
setattr(admin_class, new_fields[new_field][0],
new_fields[new_field][1])
admin_class.list_display.append(new_fields[new_field][0])
try: # pass gracefully on duplicate registration errors
admin.site.register(c, admin_class)
except Exception:
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment