summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2014-11-16 16:42:09 +0100
committerTim Graham <timograham@gmail.com>2014-11-25 13:48:50 -0500
commit2fd16232b106cf99ed42eccf57da1eed1f815d41 (patch)
treefbc824817ea1e5ad75e7b4c330a3785e73231e70
parentc3a40af6ec50e537b82ce6ca3acf5d665db76f29 (diff)
[1.6.x] Fixed #23754 -- Always allowed reference to the primary key in the admin
This change allows dynamically created inlines "Add related" button to work correcly as long as their associated foreign key is pointing to the primary key of the related model. Thanks to amorce for the report, Julien Phalip for the initial patch, and Collin Anderson for the review. Backport of f9c4e14aeca7df79991bca8ac2d743953cbd095c from master
-rw-r--r--django/contrib/admin/options.py9
-rw-r--r--docs/releases/1.4.17.txt9
-rw-r--r--docs/releases/1.5.12.txt14
-rw-r--r--docs/releases/1.6.9.txt8
-rw-r--r--docs/releases/index.txt1
-rw-r--r--tests/admin_views/admin.py5
-rw-r--r--tests/admin_views/models.py24
-rw-r--r--tests/admin_views/tests.py16
8 files changed, 64 insertions, 22 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index a0b816ccf0..34eba4d020 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -339,9 +339,9 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
except FieldDoesNotExist:
return False
- # Check whether this model is the origin of a M2M relationship
- # in which case to_field has to be the pk on this model.
- if opts.many_to_many and field.primary_key:
+ # Always allow referencing the primary key since it's already possible
+ # to get this information from the change view URL.
+ if field.primary_key:
return True
# Make sure at least one of the models registered for this site
@@ -352,8 +352,7 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
for inline in admin.inlines:
registered_models.add(inline.model)
- for related_object in (opts.get_all_related_objects(include_hidden=True) +
- opts.get_all_related_many_to_many_objects()):
+ for related_object in opts.get_all_related_objects(include_hidden=True):
related_model = related_object.model
if (any(issubclass(model, related_model) for model in registered_models) and
related_object.field.rel.get_related_field() == field):
diff --git a/docs/releases/1.4.17.txt b/docs/releases/1.4.17.txt
index f8785bcea2..6279941733 100644
--- a/docs/releases/1.4.17.txt
+++ b/docs/releases/1.4.17.txt
@@ -4,7 +4,14 @@ Django 1.4.17 release notes
*Under development*
-Django 1.4.17 ...
+Django 1.4.17 fixes a regression in the 1.4.14 security release.
Additionally, Django's vendored version of six, :mod:`django.utils.six`, has
been upgraded to the latest release (1.8.0).
+
+Bugfixes
+========
+
+* Fixed a regression with dynamically generated inlines and allowed field
+ references in the admin
+ (`#23754 <http://code.djangoproject.com/ticket/23754>`_).
diff --git a/docs/releases/1.5.12.txt b/docs/releases/1.5.12.txt
new file mode 100644
index 0000000000..a1107bedfa
--- /dev/null
+++ b/docs/releases/1.5.12.txt
@@ -0,0 +1,14 @@
+===========================
+Django 1.5.12 release notes
+===========================
+
+*Under development*
+
+Django 1.5.12 fixes a regression in the 1.5.9 security release.
+
+Bugfixes
+========
+
+* Fixed a regression with dynamically generated inlines and allowed field
+ references in the admin
+ (`#23754 <http://code.djangoproject.com/ticket/23754>`_).
diff --git a/docs/releases/1.6.9.txt b/docs/releases/1.6.9.txt
index 242894229a..08f943fa51 100644
--- a/docs/releases/1.6.9.txt
+++ b/docs/releases/1.6.9.txt
@@ -4,7 +4,13 @@ Django 1.6.9 release notes
*Under development*
-Django 1.6.9 ...
+Django 1.6.9 fixes a regression in the 1.6.6 security release.
Additionally, Django's vendored version of six, :mod:`django.utils.six`, has
been upgraded to the latest release (1.8.0).
+
+Bugfixes
+========
+
+* Fixed a regression with dynamically generated inlines and allowed field
+ references in the admin (:ticket:`23754`).
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 3bc1d04979..3e2218a8e5 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -41,6 +41,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 1.5.12
1.5.11
1.5.10
1.5.9
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index ca8a9fdbf6..33ae182a2c 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -31,7 +31,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
RelatedPrepopulated, UndeletableObject, UserMessenger, Simple, Choice,
ShortMessage, Telegram, ReferencedByParent, ChildOfReferer, M2MReference,
- ReferencedByInline, InlineReference, InlineReferer, Ingredient)
+ ReferencedByInline, InlineReference, InlineReferer, Recipe, Ingredient, NotReferenced)
def callable_year(dt_value):
@@ -756,7 +756,6 @@ site.register(UnorderedObject, UnorderedObjectAdmin)
site.register(UndeletableObject, UndeletableObjectAdmin)
site.register(ReferencedByParent)
site.register(ChildOfReferer)
-site.register(M2MReference)
site.register(ReferencedByInline)
site.register(InlineReferer, InlineRefererAdmin)
@@ -789,7 +788,9 @@ site.register(Color2, CustomTemplateFilterColorAdmin)
site.register(Simple, AttributeErrorRaisingAdmin)
site.register(UserMessenger, MessageTestingAdmin)
site.register(Choice, ChoiceList)
+site.register(Recipe)
site.register(Ingredient)
+site.register(NotReferenced)
# Register core models we need in our tests
from django.contrib.auth.models import User, Group
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 7b8af6f8d2..dd26e13fb1 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -690,11 +690,13 @@ class Choice(models.Model):
# Models for #23329
class ReferencedByParent(models.Model):
- pass
+ name = models.CharField(max_length=20, unique=True)
class ParentWithFK(models.Model):
- fk = models.ForeignKey(ReferencedByParent)
+ fk = models.ForeignKey(
+ ReferencedByParent, to_field='name', related_name='hidden+',
+ )
class ChildOfReferer(ParentWithFK):
@@ -704,13 +706,16 @@ class ChildOfReferer(ParentWithFK):
class M2MReference(models.Model):
ref = models.ManyToManyField('self')
+
# Models for #23431
class ReferencedByInline(models.Model):
- pass
+ name = models.CharField(max_length=20, unique=True)
class InlineReference(models.Model):
- fk = models.ForeignKey(ReferencedByInline, related_name='hidden+')
+ fk = models.ForeignKey(
+ ReferencedByInline, to_field='name', related_name='hidden+',
+ )
class InlineReferer(models.Model):
@@ -719,9 +724,14 @@ class InlineReferer(models.Model):
# Models for #23604
class Recipe(models.Model):
- name = models.CharField(max_length=20)
+ pass
class Ingredient(models.Model):
- name = models.CharField(max_length=20)
- recipes = models.ManyToManyField('Recipe', related_name='ingredients')
+ recipes = models.ManyToManyField(Recipe)
+
+
+# Model for #23839
+class NotReferenced(models.Model):
+ # Don't point any FK at this model.
+ pass
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 3913baa63f..565ff7db0a 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -591,26 +591,30 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
self.assertEqual(response.status_code, 400)
self.assertEqual(len(calls), 1)
- # Specifying a field referenced by another model should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/section/", {TO_FIELD_VAR: 'id'})
+ # #23839 - Primary key should always be allowed, even if the referenced model isn't registered.
+ response = self.client.get("/test_admin/admin/admin_views/notreferenced/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200)
# Specifying a field referenced by another model though a m2m should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/m2mreference/", {TO_FIELD_VAR: 'id'})
+ # XXX: We're not testing against a non-primary key field since the admin doesn't
+ # support it yet, ref #23862
+ response = self.client.get("/test_admin/admin/admin_views/recipe/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200)
- # #23604 - Specifying the pk of this model should be allowed when this model defines a m2m relationship
+ # #23604 - Specifying a field referenced through a reverse m2m relationship should be allowed.
+ # XXX: We're not testing against a non-primary key field since the admin doesn't
+ # support it yet, ref #23862
response = self.client.get("/test_admin/admin/admin_views/ingredient/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200)
# #23329 - Specifying a field that is not refered by any other model directly registered
# to this admin site but registered through inheritance should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/referencedbyparent/", {TO_FIELD_VAR: 'id'})
+ response = self.client.get("/test_admin/admin/admin_views/referencedbyparent/", {TO_FIELD_VAR: 'name'})
self.assertEqual(response.status_code, 200)
# #23431 - Specifying a field that is only refered to by a inline of a registered
# model should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/referencedbyinline/", {TO_FIELD_VAR: 'id'})
+ response = self.client.get("/test_admin/admin/admin_views/referencedbyinline/", {TO_FIELD_VAR: 'name'})
self.assertEqual(response.status_code, 200)
def test_allowed_filtering_15103(self):