mirror of https://github.com/django/django.git
Fixed #13163 -- Added ability to show change links on inline objects in admin.
Thanks DrMeers for the suggestion.
This commit is contained in:
parent
9a922dcad1
commit
9d9f0acd7e
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue