Daniel Rhoden
Daniel Rhoden

Reputation: 6127

How to add 'collapse' to a Django StackedInline

In the same way you can add 'classes': ['collapse'] to one of your ModelAdmin fieldsets, I'd like to be able to have an Inline Model Admin be collapsible.

This ticket, Collapse in admin interface for inline related objects, discusses exactly what I want to accomplish. But in the mean time, what's the best work around while we wait for the next release?

FYI: I've come up with a solution, but I think a better one exists. I'll let the voting take care of it.

Upvotes: 17

Views: 13435

Answers (8)

Dharm Shah
Dharm Shah

Reputation: 61

If you want collapsible inline model then,

class AAAAdmin(admin.StackedInline):
    model = AAA
    classes = ['collapse', 'show']

If you want the grouping of fields collapsible then,

fieldsets = (
    ("Basic Details", {'fields': (
        "title", "street_line1", "street_line2", "city", "state", "country", "zipcode",
        "additional_code", "zone", "contact_no"
    )}),
    ("Google Map Related Details", {"classes": ['collapse', 'show'], 'fields': (
        "location", "longitude", "latitude", "google_map_link"
    )}),
)

Upvotes: 0

aris
aris

Reputation: 24285

In modern-day Django this is as easy as the following:

class FooInline(admin.StackedInline):
     model = Foo
     classes = ['collapse']

Upvotes: 4

Sagar Ramachandrappa
Sagar Ramachandrappa

Reputation: 1461

From django 1.10, We can now add extra css classes to InlineModelAdmin as well.

A list or tuple containing extra CSS classes to apply to the fieldset that is rendered for the inlines. Defaults to None. As with classes configured in fieldsets, inlines with a collapse class will be initially collapsed and their header will have a small “show” link.

Docs

Upvotes: 3

danbgray
danbgray

Reputation: 582

You can use grappelli - which supports collapsing fieldsets. It uses a solution much like the solutions mentioned above, but the javascript / coding is already done - you just have to add 'classes':(collapse closed',), to your fieldset ( see http://readthedocs.org/docs/django-grappelli/en/latest/customization.html)

for example:

class ModelOptions(admin.ModelAdmin):
    fieldsets = (
        ('', {
            'fields': ('title', 'subtitle', 'slug', 'pub_date', 'status',),
        }),
        ('Flags', {
            'classes': ('grp-collapse grp-closed',),
            'fields' : ('flag_front', 'flag_sticky', 'flag_allow_comments', 'flag_comments_closed',),
        }),
        ('Tags', {
            'classes': ('grp-collapse grp-open',),
            'fields' : ('tags',),
        }),
    )

class StackedItemInline(admin.StackedInline):
    classes = ('grp-collapse grp-open',)

class TabularItemInline(admin.TabularInline):
    classes = ('grp-collapse grp-open',)

Upvotes: 5

Matthew Schinckel
Matthew Schinckel

Reputation: 35619

My current solution, based on others listed here, has the following features:

  • Only collapses stacked inlines
  • Does not collapse inlines that contain an error
  • Does not collapse the 'empty' inline.

It's a Javascript solution, which means it needs to be injected into your page/template somehow.

It requires jQuery be loaded on the page by the time it is executed. Modern versions of Django have this.

$(function(){
  // Find all stacked inlines (they have an h3, with a span.inline_label).
  // Add a link to toggle collapsed state.
  $('.inline-group h3 .inline_label').append(' (<a class="collapse-toggle" href="#">Show</a>)');
  // Collapse all fieldsets that are in a stacked inline (not .tabular)
  $('.inline-group :not(.tabular) fieldset').addClass('collapsed');
  // Click handler: toggle the related fieldset, and the content of our link.
  $('.inline-group h3 .inline_label .collapse-toggle').on('click', function(evt) {
    $(this).closest('.inline-related').find('fieldset').toggleClass('collapsed');
    text = $(this).html();
    if (text=='Show') {
      $(this).html('Hide');
    } else {
      $(this).html('Show');
    };
    evt.preventDefault();
    evt.stopPropagation();
  });
  // Un-collapse empty forms, otherwise it's 2 clicks to create a new one.
  $('.empty-form .collapse-toggle').click();
  // Un-collapse any objects with errors.
  $('.inline-group .errors').closest('.inline-related').find('.collapse-toggle').click();
});

Upvotes: 3

nosforz
nosforz

Reputation: 39

A couple of improvements on gerdemb's answer. Adds the 'Show' and 'Hide' text appropriately, and lets you specify the tabular inline names in a list beforehand:

$(document).ready(function(){
var tabNames = ['Inline Name 1', 'Inline Name 2', 'Inline Name 3'];
for (var x in tabNames)
{
    var selector = "h2:contains(" + tabNames[x] + ")";
    $(selector).parent().addClass("collapsed");
    $(selector).append(" (<a class=\"collapse-toggle\" id=\"customcollapser\""+ x + " href=\"#\"> Show </a>)");
};    
$(".collapse-toggle").click(function(e) {
    $(this).parent().parent().toggleClass("collapsed");
    var text = $(this).html();
    if (text==' Show ') {
        $(this).html(' Hide ');
        }
    else {
        $(this).html(' Show ');
    };
    e.preventDefault();
});
});

Upvotes: 2

gerdemb
gerdemb

Reputation: 11477

I came up with this solution using jQuery that works on TabularInline

var selector = "h2:contains('TITLE_OF_INLINE_BLOCK')";
$(selector).parent().addClass("collapsed");
$(selector).append(" (<a class=\"collapse-toggle\" id=\"customcollapser\" href=\"#\"> Show </a>)");
$("#customcollapser").click(function() {
    $(selector).parent().toggleClass("collapsed");
});

Upvotes: 4

Daniel Rhoden
Daniel Rhoden

Reputation: 6127

Here's how I solved it, but it feels too much like a hack (for a hack).

I used jQuery hosted from Google APIs to modify the DOM, taking advantage of Django's own 'show/hide' script. If you look at the html source of an admin page, the last script loaded is this:

<script type="text/javascript" src="/media/admin/js/admin/CollapsedFieldsets.js"></script>

The comments in that file gave me the idea: Leverage ModelAdmin media definitions to load my own dom-altering script.

from django.contrib import admin
from django.contrib.admin.sites import AdminSite
from myapp.models import *
import settings
media = settings.MEDIA_URL

class MyParticularModelAdmin(admin.ModelAdmin):
    # .....
    class Media:
          js = ('http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js',
              media+'js/addCollapseToAllStackedInlines.js')
# .....

And then inside of the referenced javascript file:

// addCollapseToAllStackedInlines.js
$(document).ready(function() {
  $("div.inline-group").wrapInner("<fieldset class=\"module aligned collapse\"></fieldset>");
});

The end results only works on StackedInline, NOT TabularInline.

Upvotes: 2

Related Questions