[Django]-Django InlineModelAdmin: Show partially an inline model and link to the complete model

29👍

UPDATE:
Since Django 1.8, this is built in.

See this answer and the official documentation.

OLD ANSWER:

At the end I found a simple solution.

I create a new template called linked.html that is a copy of tabular.html and I added this code to create the link.

{% if inline_admin_form.original.pk %}
          <td class="{{ field.field.name }}">
              <a href="/admin/{{ app_label }}/{{ inline_admin_formset.opts.admin_model_path }}/{{ inline_admin_form.original.pk }}/">Full record</a>
          </td>
{% endif %}

then I created a new model LinkedInline inheriting InlineModelAdmin

#override of the InlineModelAdmin to support the link in the tabular inline
class LinkedInline(admin.options.InlineModelAdmin):
    template = "admin/linked.html"
    admin_model_path = None

    def __init__(self, *args):
        super(LinkedInline, self).__init__(*args)
        if self.admin_model_path is None:
            self.admin_model_path = self.model.__name__.lower()

Then when I define a new inline, I have only to use my LinkedInline instead of the normal InlineModelAdmin.

I hope it can be useful for other people.

Giovanni

25👍

Update:

As of Django 1.8, this is now built-in.

Answer for Django <= 1.7:

Keep your code in models.py with conditional case:

def selflink(self):
    if self.id:
        return "<a href='/link/to/volume/%s' target='_blank'>Edit</a>" % str(self.id)
    else:
        return "Not present"

selflink.allow_tags = True

In admin.py, add selflink as readonly field:

class VolumeInline(admin.TabularInline):
    readonly_fields = ['selflink',]
    model = Volume

That worked for me.

14👍

Here’s a reusable mixin based on some of the other answers. This is handy because it works with both Tabular and Stacked inlines, and doesn’t cruft up your model or admin code.

# put this somewhere like admin_helpers.py
from django.core.urlresolvers import reverse

class InlineEditLinkMixin(object):
    readonly_fields = ['edit_details']
    edit_label = "Edit"
    def edit_details(self, obj):
        if obj.id:
            opts = self.model._meta
            return "<a href='%s' target='_blank'>%s</a>" % (reverse(
                'admin:%s_%s_change' % (opts.app_label, opts.object_name.lower()),
                args=[obj.id]
            ), self.edit_label)
        else:
            return "(save to edit details)"
    edit_details.allow_tags = True

# admin.py

class VolumeInline(InlineEditLinkMixin, admin.TabularInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [VolumeInline]

class ScanInfoInline(InlineEditLinkMixin, admin.StackedInline):
    fields = ['foo', 'bar', 'edit_details']

class JournalAdmin(admin.ModelAdmin):
    inlines = [ScanInfoInline]

9👍

In Django 1.8+ this is now much easier. Just add show_change_link = True to your TabularInline or StackedInline subclass, like this:

class VolumeInline(admin.TabularInline):
    fields = ['volumenumber']
    model = Volume
    extra = 1
    show_change_link = True

And Django will automatically add a link to the full change form for each inline item, if the model has its own registered ModelAdmin.

3👍

Did you try the Reversing admin URL system ?
That could give something like that (in the journal page):

<ul>
{% for volume in original.volume_set.all %}
  <li>
    <a href="{% url admin:yourapp_volume_change volume.id %}">Edit {{ volume }}
    </a>
  </li>
{% endfor %}
</ul>

2👍

After some fiddling, I was able to make this work in an InlineAdmin and a TabularInline, using reverse(). At least with TabularInline, the field you want to link must be listed in ‘readonly_fields’

# create a read-only inline with the first field linked
from django.core import urlresolvers
class YOUR_MODEL_Inline(LinkedTabularInline):
    max_num = 0 # remove the "Add another ..." link
    model = YOUR_MODEL_NAME
    fk_name = "YOUR_FOREIGN_KEY_NAME"
    fields = [ 'link_name', ] # , 'field1', 'field2', 'etc' ]
    readonly_fields = fields
    can_delete = False
    def link_name(self, obj):
        if obj.pk:
            url = urlresolvers.reverse('admin:%s_%s_change'
                % (obj._meta.app_label, obj._meta.module_name), args=[obj.id])
            # obj.MODEL_FIELD can be a text string or whatever you want
            return '<a href="{0}">{1}</a>'.format(url, obj.MODEL_FIELD) 
    link_name.allow_tags = True
    link_name.short_description = "MODEL_FIELD"

If you want to link to the change list instead of the change view, you can modify the reverse() call. changelist does not require an object id.

    url = urlresolvers.reverse('admin:%s_%s_changelist' 
        % (obj._meta.app_label, obj._meta.module_name))
    return '<a href="{0}">{1}</a>'.format(url, obj.name)

And if you want to link to a subset of objects, you can add parameters to the URL:

    return '<a href="{0}?YOUR_MODEL_FIELD__id__exact={1}">{2}</a>'.format(url, obj.id, obj.name)

Leave a comment