diff options
author | Nick Sandford <nick@sandford.id.au> | 2014-07-25 13:07:04 +0100 |
---|---|---|
committer | Tim Graham <timograham@gmail.com> | 2014-07-31 08:07:28 -0400 |
commit | 9d9f0acd7e245c9f9c30727a003666618754c924 (patch) | |
tree | 719c97995bfa06038e3e72efbb0c106f3e73861d | |
parent | 9a922dcad1e50afc91329009e8689e5c08c2a1bf (diff) | |
download | django-9d9f0acd7e245c9f9c30727a003666618754c924.tar.gz |
Fixed #13163 -- Added ability to show change links on inline objects in admin.
Thanks DrMeers for the suggestion.
-rw-r--r-- | django/contrib/admin/options.py | 2 | ||||
-rw-r--r-- | django/contrib/admin/sites.py | 6 | ||||
-rw-r--r-- | django/contrib/admin/static/admin/css/base.css | 2 | ||||
-rw-r--r-- | django/contrib/admin/templates/admin/edit_inline/stacked.html | 5 | ||||
-rw-r--r-- | django/contrib/admin/templates/admin/edit_inline/tabular.html | 7 | ||||
-rw-r--r-- | docs/ref/contrib/admin/index.txt | 7 | ||||
-rw-r--r-- | docs/releases/1.8.txt | 4 | ||||
-rw-r--r-- | tests/admin_inlines/admin.py | 3 | ||||
-rw-r--r-- | tests/admin_inlines/tests.py | 36 | ||||
-rw-r--r-- | tests/admin_ordering/tests.py | 10 | ||||
-rw-r--r-- | tests/admin_registration/tests.py | 9 | ||||
-rw-r--r-- | tests/generic_inline_admin/tests.py | 3 |
12 files changed, 81 insertions, 13 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 16be58e15d..f304be0b81 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1721,6 +1721,7 @@ class InlineModelAdmin(BaseModelAdmin): verbose_name = None verbose_name_plural = None can_delete = True + show_change_link = False checks_class = InlineModelAdminChecks @@ -1728,6 +1729,7 @@ class InlineModelAdmin(BaseModelAdmin): self.admin_site = admin_site self.parent_model = parent_model self.opts = self.model._meta + self.has_registered_model = admin_site.is_registered(self.model) super(InlineModelAdmin, self).__init__() if self.verbose_name is None: self.verbose_name = self.model._meta.verbose_name diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 84778849e9..666f1da1fc 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -114,6 +114,12 @@ class AdminSite(object): raise NotRegistered('The model %s is not registered' % model.__name__) del self._registry[model] + def is_registered(self, model): + """ + Check if a model class is registered with this `AdminSite`. + """ + return model in self._registry + def add_action(self, action, name=None): """ Register an action to be available globally. diff --git a/django/contrib/admin/static/admin/css/base.css b/django/contrib/admin/static/admin/css/base.css index ca9fa501ea..3042ae0d62 100644 --- a/django/contrib/admin/static/admin/css/base.css +++ b/django/contrib/admin/static/admin/css/base.css @@ -632,7 +632,7 @@ div.breadcrumbs { background: url(../img/icon_addlink.gif) 0 .2em no-repeat; } -.changelink { +.changelink, .inlinechangelink { padding-left: 12px; background: url(../img/icon_changelink.gif) 0 .2em no-repeat; } diff --git a/django/contrib/admin/templates/admin/edit_inline/stacked.html b/django/contrib/admin/templates/admin/edit_inline/stacked.html index 79c052c6c5..9d58146588 100644 --- a/django/contrib/admin/templates/admin/edit_inline/stacked.html +++ b/django/contrib/admin/templates/admin/edit_inline/stacked.html @@ -1,11 +1,12 @@ -{% load i18n admin_static %} +{% load i18n admin_urls admin_static %} <div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group"> <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2> {{ inline_admin_formset.formset.management_form }} {{ inline_admin_formset.formset.non_form_errors }} {% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}"> - <h3><b>{{ inline_admin_formset.opts.verbose_name|capfirst }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span> + <h3><b>{{ inline_admin_formset.opts.verbose_name|capfirst }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %} <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="inlinechangelink">{% trans "Change" %}</a>{% endif %} +{% else %}#{{ forloop.counter }}{% endif %}</span> {% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %} {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %} </h3> diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html index 4548af45dc..c1a07cf76f 100644 --- a/django/contrib/admin/templates/admin/edit_inline/tabular.html +++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html @@ -1,4 +1,4 @@ -{% load i18n admin_static admin_modify %} +{% load i18n admin_urls admin_static admin_modify %} <div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group"> <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}"> {{ inline_admin_formset.formset.management_form }} @@ -26,7 +26,10 @@ id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}"> <td class="original"> {% if inline_admin_form.original or inline_admin_form.show_url %}<p> - {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %} + {% if inline_admin_form.original %} + {{ inline_admin_form.original }} + {% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}<a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="inlinechangelink">{% trans "Change" %}</a>{% endif %} + {% endif %} {% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %} </p>{% endif %} {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %} diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index d878058071..958551d660 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -2025,6 +2025,13 @@ The ``InlineModelAdmin`` class adds: Specifies whether or not inline objects can be deleted in the inline. Defaults to ``True``. +.. attribute:: InlineModelAdmin.show_change_link + + .. versionadded:: 1.8 + + Specifies whether or not inline objects that can be changed in the + admin have a link to the change form. Defaults to ``False``. + .. method:: InlineModelAdmin.get_formset(request, obj=None, **kwargs) Returns a :class:`~django.forms.models.BaseInlineFormSet` class for use in diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 690480a4e1..d4943fd0da 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -35,6 +35,10 @@ Minor features :meth:`~django.contrib.admin.ModelAdmin.has_module_permission` method to allow limiting access to the module on the admin index page. +* :class:`~django.contrib.admin.InlineModelAdmin` now has an attribute + :attr:`~django.contrib.admin.InlineModelAdmin.show_change_link` that + supports showing a link to an inline object's change form. + :mod:`django.contrib.auth` ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index b46a045052..4fc7478132 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -90,10 +90,12 @@ class TitleInline(admin.TabularInline): class Inner4StackedInline(admin.StackedInline): model = Inner4Stacked + show_change_link = True class Inner4TabularInline(admin.TabularInline): model = Inner4Tabular + show_change_link = True class Holder4Admin(admin.ModelAdmin): @@ -212,3 +214,4 @@ site.register(ParentModelWithCustomPk, inlines=[ChildModel1Inline, ChildModel2In site.register(BinaryTree, inlines=[BinaryTreeAdmin]) site.register(ExtraTerrestrial, inlines=[SightingInline]) site.register(SomeParentModel, inlines=[SomeChildModelInline]) +site.register([Question, Inner4Stacked, Inner4Tabular]) diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 56f006c441..9ed54fd091 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -13,7 +13,9 @@ from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person, OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile, ProfileCollection, ParentModelWithCustomPk, ChildModel1, ChildModel2, Sighting, Novel, Chapter, FootNote, BinaryTree, SomeParentModel, - SomeChildModel) + SomeChildModel, Poll, Question, Inner4Stacked, Inner4Tabular, Holder4) + +INLINE_CHANGELINK_HTML = 'class="inlinechangelink">Change</a>' @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), @@ -311,6 +313,38 @@ class TestInline(TestCase): count=1 ) + def test_inlines_show_change_link_registered(self): + "Inlines `show_change_link` for registered models when enabled." + holder = Holder4.objects.create(dummy=1) + item1 = Inner4Stacked.objects.create(dummy=1, holder=holder) + item2 = Inner4Tabular.objects.create(dummy=1, holder=holder) + items = ( + ('inner4stacked', item1.pk), + ('inner4tabular', item2.pk), + ) + response = self.client.get('/admin/admin_inlines/holder4/%s/' % holder.pk) + self.assertTrue(response.context['inline_admin_formset'].opts.has_registered_model) + for model, pk in items: + url = '/admin/admin_inlines/%s/%s/' % (model, pk) + self.assertContains(response, '<a href="%s" %s' % (url, INLINE_CHANGELINK_HTML)) + + def test_inlines_show_change_link_unregistered(self): + "Inlines `show_change_link` disabled for unregistered models." + parent = ParentModelWithCustomPk.objects.create(my_own_pk="foo", name="Foo") + ChildModel1.objects.create(my_own_pk="bar", name="Bar", parent=parent) + ChildModel2.objects.create(my_own_pk="baz", name="Baz", parent=parent) + response = self.client.get('/admin/admin_inlines/parentmodelwithcustompk/foo/') + self.assertFalse(response.context['inline_admin_formset'].opts.has_registered_model) + self.assertNotContains(response, INLINE_CHANGELINK_HTML) + + def test_tabular_inline_show_change_link_false_registered(self): + "Inlines `show_change_link` disabled by default." + poll = Poll.objects.create(name="New poll") + Question.objects.create(poll=poll) + response = self.client.get('/admin/admin_inlines/poll/%s/' % poll.pk) + self.assertTrue(response.context['inline_admin_formset'].opts.has_registered_model) + self.assertNotContains(response, INLINE_CHANGELINK_HTML) + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="admin_inlines.urls") diff --git a/tests/admin_ordering/tests.py b/tests/admin_ordering/tests.py index bc08adee12..1bd06e6e5a 100644 --- a/tests/admin_ordering/tests.py +++ b/tests/admin_ordering/tests.py @@ -44,7 +44,7 @@ class TestAdminOrdering(TestCase): The default ordering should be by name, as specified in the inner Meta class. """ - ma = ModelAdmin(Band, None) + ma = ModelAdmin(Band, admin.site) names = [b.name for b in ma.get_queryset(request)] self.assertListEqual(['Aerosmith', 'Radiohead', 'Van Halen'], names) @@ -55,7 +55,7 @@ class TestAdminOrdering(TestCase): """ class BandAdmin(ModelAdmin): ordering = ('rank',) # default ordering is ('name',) - ma = BandAdmin(Band, None) + ma = BandAdmin(Band, admin.site) names = [b.name for b in ma.get_queryset(request)] self.assertListEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names) @@ -67,7 +67,7 @@ class TestAdminOrdering(TestCase): other_user = User.objects.create(username='other') request = self.request_factory.get('/') request.user = super_user - ma = DynOrderingBandAdmin(Band, None) + ma = DynOrderingBandAdmin(Band, admin.site) names = [b.name for b in ma.get_queryset(request)] self.assertListEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names) request.user = other_user @@ -94,7 +94,7 @@ class TestInlineModelAdminOrdering(TestCase): The default ordering should be by name, as specified in the inner Meta class. """ - inline = SongInlineDefaultOrdering(self.band, None) + inline = SongInlineDefaultOrdering(self.band, admin.site) names = [s.name for s in inline.get_queryset(request)] self.assertListEqual(['Dude (Looks Like a Lady)', 'Jaded', 'Pink'], names) @@ -102,7 +102,7 @@ class TestInlineModelAdminOrdering(TestCase): """ Let's check with ordering set to something different than the default. """ - inline = SongInlineNewOrdering(self.band, None) + inline = SongInlineNewOrdering(self.band, admin.site) names = [s.name for s in inline.get_queryset(request)] self.assertListEqual(['Jaded', 'Pink', 'Dude (Looks Like a Lady)'], names) diff --git a/tests/admin_registration/tests.py b/tests/admin_registration/tests.py index e5947e0a93..98a509fc42 100644 --- a/tests/admin_registration/tests.py +++ b/tests/admin_registration/tests.py @@ -70,6 +70,15 @@ class TestRegistration(TestCase): """ self.assertRaises(ImproperlyConfigured, self.site.register, Location) + def test_is_registered_model(self): + "Checks for registered models should return true." + self.site.register(Person) + self.assertTrue(self.site.is_registered(Person)) + + def test_is_registered_not_registered_model(self): + "Checks for unregistered models should return false." + self.assertFalse(self.site.is_registered(Person)) + class TestRegistrationDecorator(TestCase): """ diff --git a/tests/generic_inline_admin/tests.py b/tests/generic_inline_admin/tests.py index 99459b9257..dafd5d3334 100644 --- a/tests/generic_inline_admin/tests.py +++ b/tests/generic_inline_admin/tests.py @@ -256,8 +256,7 @@ class GenericInlineAdminWithUniqueTogetherTest(TestCase): class NoInlineDeletionTest(TestCase): def test_no_deletion(self): - fake_site = object() - inline = MediaPermanentInline(EpisodePermanent, fake_site) + inline = MediaPermanentInline(EpisodePermanent, admin_site) fake_request = object() formset = inline.get_formset(fake_request) self.assertFalse(formset.can_delete) |