Login

Readonly fields with link to change page in admin

Author:
hchargois
Posted:
December 11, 2015
Language:
Python
Version:
1.7
Tags:
admin
Score:
0 (after 0 ratings)

Usage :

class MyModelAdmin(ReadonlyLinksMixin, admin.ModelAdmin):
    readonly_fields_links = ('field1', 'field2')

This adds a new ModelAdmin property (readonly_fields_links) that acts like the default readonly_links except that (if the field's type is a model that can be edited in the admin site) the value of the field has a link to the object.

Same functionality as

Except that it works without messing with the form that gets validated and saved, and thus without sometimes saving None values. It uses the documented property that readonly_fields can be callables (Django doc) : the fields in readonly_links_fields are turned into callables that are appended to readonly_links. Each callable creates the linked value.

 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
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('<a href="%s">%s</a>' % (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

More like this

Comments

Please login first before commenting.