Fixed #494 -- Added ability to specify classes on admin inline fieldsets.

This includes the ability to collapse inlines by specifying a class named
'collapse'.
This commit is contained in:
Karen Tracey 2015-11-07 10:46:50 -05:00 committed by Tim Graham
parent 63a6a653d4
commit 5399ccc0f4
8 changed files with 43 additions and 1 deletions

View File

@ -235,6 +235,7 @@ class InlineAdminFormSet(object):
if prepopulated_fields is None: if prepopulated_fields is None:
prepopulated_fields = {} prepopulated_fields = {}
self.prepopulated_fields = prepopulated_fields self.prepopulated_fields = prepopulated_fields
self.classes = ' '.join(inline.classes) if inline.classes else ''
def __iter__(self): def __iter__(self):
for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):

View File

@ -1800,6 +1800,7 @@ class InlineModelAdmin(BaseModelAdmin):
can_delete = True can_delete = True
show_change_link = False show_change_link = False
checks_class = InlineModelAdminChecks checks_class = InlineModelAdminChecks
classes = None
def __init__(self, parent_model, admin_site): def __init__(self, parent_model, admin_site):
self.admin_site = admin_site self.admin_site = admin_site
@ -1819,6 +1820,8 @@ class InlineModelAdmin(BaseModelAdmin):
'inlines%s.js' % extra] 'inlines%s.js' % extra]
if self.filter_vertical or self.filter_horizontal: if self.filter_vertical or self.filter_horizontal:
js.extend(['SelectBox.js', 'SelectFilter2.js']) js.extend(['SelectBox.js', 'SelectFilter2.js'])
if self.classes and 'collapse' in self.classes:
js.append('collapse%s.js' % extra)
return forms.Media(js=['admin/js/%s' % url for url in js]) return forms.Media(js=['admin/js/%s' % url for url in js])
def get_extra(self, request, obj=None, **kwargs): def get_extra(self, request, obj=None, **kwargs):

View File

@ -3,6 +3,7 @@
id="{{ inline_admin_formset.formset.prefix }}-group" id="{{ inline_admin_formset.formset.prefix }}-group"
data-inline-type="stacked" data-inline-type="stacked"
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"> data-inline-formset="{{ inline_admin_formset.inline_formset_data }}">
<fieldset class="module {{ inline_admin_formset.classes }}">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2> <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
{{ inline_admin_formset.formset.management_form }} {{ inline_admin_formset.formset.management_form }}
{{ inline_admin_formset.formset.non_form_errors }} {{ inline_admin_formset.formset.non_form_errors }}
@ -20,4 +21,5 @@
{% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %} {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }} {{ inline_admin_form.fk_field.field }}
</div>{% endfor %} </div>{% endfor %}
</fieldset>
</div> </div>

View File

@ -4,7 +4,7 @@
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"> data-inline-formset="{{ inline_admin_formset.inline_formset_data }}">
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}"> <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
{{ inline_admin_formset.formset.management_form }} {{ inline_admin_formset.formset.management_form }}
<fieldset class="module"> <fieldset class="module {{ inline_admin_formset.classes }}">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2> <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
{{ inline_admin_formset.formset.non_form_errors }} {{ inline_admin_formset.formset.non_form_errors }}
<table> <table>

View File

@ -2058,6 +2058,16 @@ The ``InlineModelAdmin`` class adds:
parent model fails to validate, it may be left in an inconsistent state as parent model fails to validate, it may be left in an inconsistent state as
described in the warning in :ref:`validation-on-modelform`. described in the warning in :ref:`validation-on-modelform`.
.. attribute:: InlineModelAdmin.classes
.. versionadded:: 1.10
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 :attr:`~ModelAdmin.fieldsets`, inlines with a ``collapse``
class will be initially collapsed and their header will have a small "show"
link.
.. attribute:: InlineModelAdmin.extra .. attribute:: InlineModelAdmin.extra
This controls the number of extra forms the formset will display in This controls the number of extra forms the formset will display in

View File

@ -42,6 +42,11 @@ Minor features
* All inline JavaScript is removed so you can enable the * All inline JavaScript is removed so you can enable the
``Content-Security-Policy`` HTTP header if you wish. ``Content-Security-Policy`` HTTP header if you wish.
* The new :attr:`InlineModelAdmin.classes
<django.contrib.admin.InlineModelAdmin.classes>` attribute allows specifying
classes on inline fieldsets. Inlines with a ``collapse`` class will be
initially collapsed and their header will have a small "show" link.
:mod:`django.contrib.admindocs` :mod:`django.contrib.admindocs`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -20,10 +20,12 @@ class BookInline(admin.TabularInline):
class NonAutoPKBookTabularInline(admin.TabularInline): class NonAutoPKBookTabularInline(admin.TabularInline):
model = NonAutoPKBook model = NonAutoPKBook
classes = ('collapse',)
class NonAutoPKBookStackedInline(admin.StackedInline): class NonAutoPKBookStackedInline(admin.StackedInline):
model = NonAutoPKBook model = NonAutoPKBook
classes = ('collapse',)
class EditablePKBookTabularInline(admin.TabularInline): class EditablePKBookTabularInline(admin.TabularInline):

View File

@ -874,6 +874,25 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
self.assertEqual(len(self.selenium.find_elements_by_css_selector( self.assertEqual(len(self.selenium.find_elements_by_css_selector(
"%s.row2" % row_selector)), 1, msg="Expect one row2 styled row") "%s.row2" % row_selector)), 1, msg="Expect one row2 styled row")
def test_collapsed_inlines(self):
# Collapsed inlines have SHOW/HIDE links.
self.admin_login(username='super', password='secret')
self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_author_add'))
# One field is in a stacked inline, other in a tabular one.
test_fields = ['id_nonautopkbook_set-0-title', 'id_nonautopkbook_set-2-0-title']
show_links = self.selenium.find_elements_by_link_text('SHOW')
self.assertEqual(len(show_links), 2)
for show_index, field_name in enumerate(test_fields, 0):
self.assertFalse(self.selenium.find_element_by_id(field_name).is_displayed())
show_links[show_index].click()
self.assertTrue(self.selenium.find_element_by_id(field_name).is_displayed())
hide_links = self.selenium.find_elements_by_link_text('HIDE')
self.assertEqual(len(hide_links), 2)
for hide_index, field_name in enumerate(test_fields, 0):
self.assertTrue(self.selenium.find_element_by_id(field_name).is_displayed())
hide_links[hide_index].click()
self.assertFalse(self.selenium.find_element_by_id(field_name).is_displayed())
class SeleniumChromeTests(SeleniumFirefoxTests): class SeleniumChromeTests(SeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver' webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'