diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 320d3267a78..3ffb85e6c63 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -266,10 +266,12 @@ class InlineAdminForm(AdminForm): yield InlineFieldset(self.formset, self.form, name, self.readonly_fields, model_admin=self.model_admin, **options) - def has_auto_field(self): - if self.form._meta.model._meta.has_auto_field: + def needs_explicit_pk_field(self): + # Auto fields are editable (oddly), so need to check for auto or non-editable pk + if self.form._meta.model._meta.has_auto_field or not self.form._meta.model._meta.pk.editable: return True - # Also search any parents for an auto field. + # Also search any parents for an auto field. (The pk info is propagated to child + # models so that does not need to be checked in parents.) for parent in self.form._meta.model._meta.get_parent_list(): if parent._meta.has_auto_field: return True diff --git a/django/contrib/admin/templates/admin/edit_inline/stacked.html b/django/contrib/admin/templates/admin/edit_inline/stacked.html index 2025dd8083b..32917756b3f 100644 --- a/django/contrib/admin/templates/admin/edit_inline/stacked.html +++ b/django/contrib/admin/templates/admin/edit_inline/stacked.html @@ -13,7 +13,7 @@ {% for fieldset in inline_admin_form %} {% include "admin/includes/fieldset.html" %} {% endfor %} - {% if inline_admin_form.has_auto_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 }} {% endfor %} diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html index ca853bc9894..e6097f0dce0 100644 --- a/django/contrib/admin/templates/admin/edit_inline/tabular.html +++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html @@ -29,7 +29,7 @@ {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %} {% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %}

{% endif %} - {% if inline_admin_form.has_auto_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 }} {% spaceless %} {% for fieldset in inline_admin_form %} diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index 2f88248ca4a..62f9e04e5bb 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -12,8 +12,26 @@ class BookInline(admin.TabularInline): model = Author.books.through +class NonAutoPKBookTabularInline(admin.TabularInline): + model = NonAutoPKBook + + +class NonAutoPKBookStackedInline(admin.StackedInline): + model = NonAutoPKBook + + +class EditablePKBookTabularInline(admin.TabularInline): + model = EditablePKBook + + +class EditablePKBookStackedInline(admin.StackedInline): + model = EditablePKBook + + class AuthorAdmin(admin.ModelAdmin): - inlines = [BookInline] + inlines = [BookInline, + NonAutoPKBookTabularInline, NonAutoPKBookStackedInline, + EditablePKBookTabularInline, EditablePKBookStackedInline] class InnerInline(admin.StackedInline): diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py index d4ba0ab6bc1..dde5cf79e30 100644 --- a/tests/admin_inlines/models.py +++ b/tests/admin_inlines/models.py @@ -3,6 +3,7 @@ Testing of admin inline formsets. """ from __future__ import unicode_literals +import random from django.db import models from django.contrib.contenttypes.models import ContentType @@ -48,6 +49,25 @@ class Author(models.Model): books = models.ManyToManyField(Book) +class NonAutoPKBook(models.Model): + rand_pk = models.IntegerField(primary_key=True, editable=False) + author = models.ForeignKey(Author) + title = models.CharField(max_length=50) + + def save(self, *args, **kwargs): + while not self.rand_pk: + test_pk = random.randint(1, 99999) + if not NonAutoPKBook.objects.filter(rand_pk=test_pk).exists(): + self.rand_pk = test_pk + super(NonAutoPKBook, self).save(*args, **kwargs) + + +class EditablePKBook(models.Model): + manual_pk = models.IntegerField(primary_key=True) + author = models.ForeignKey(Author) + title = models.CharField(max_length=50) + + class Holder(models.Model): dummy = models.IntegerField() diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 41dc0e32dea..465b224d4f0 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -211,6 +211,24 @@ class TestInline(TestCase): self.assertContains(response, max_forms_input % 2) self.assertContains(response, total_forms_hidden) + def test_inline_nonauto_noneditable_pk(self): + response = self.client.get('/admin/admin_inlines/author/add/') + self.assertContains(response, + '', + html=True) + self.assertContains(response, + '', + html=True) + + def test_inline_editable_pk(self): + response = self.client.get('/admin/admin_inlines/author/add/') + self.assertContains(response, + '', + html=True, count=1) + self.assertContains(response, + '', + html=True, count=1) + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class TestInlineMedia(TestCase):