from django.utils.safestring import mark_safe from django.utils.html import escape from django.core import urlresolvers class ReadonlyLinksMixin(object): @staticmethod def _make_link_widget(field_name, admin_view): def link_widget(instance): field = getattr(instance, field_name) if field is None: return "None" link = urlresolvers.reverse(admin_view, args=(field.id,)) return mark_safe('%s' % (link, escape(unicode(field)))) return link_widget def get_readonly_fields(self, request, obj=None): # get_readonly_fields gets called multiple times and they MUST return EXACTLY the same # fields because if not Django will break later on when doing some sneaky "let's subtract these fields # to those fields to see if there's a difference and if I'm missing something" # Exactly the same fields means, for the callables, that they must be defined only once. # We thus use some caching/memoization. try: return self._link_readonly_fields except AttributeError: pass initial_readonly_fields = super(ReadonlyLinksMixin, self).get_readonly_fields(request, obj) if not initial_readonly_fields: initial_readonly_fields = [] link_readonly_fields = [] if hasattr(self, 'readonly_fields_links'): for field_name in self.readonly_fields_links: try: field = getattr(obj, field_name) app_label = field._meta.app_label model_name = field._meta.model_name except AttributeError: # probably a native type and not a django model, # let's set it as a normal readonly_field link_readonly_fields.append(field_name) continue admin_view = 'admin:%s_%s_change' % (app_label, model_name) link_widget = self._make_link_widget(field_name, admin_view) link_widget.short_description = obj._meta.get_field(field_name).verbose_name link_readonly_fields.append(link_widget) if self.exclude is None: self.exclude = [field_name] elif field_name not in self.exclude: self.exclude = list(self.exclude) + [field_name] link_readonly_fields.extend(initial_readonly_fields) self._link_readonly_fields = link_readonly_fields return link_readonly_fields