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.
This commit is contained in:
parent
e0d1f2684a
commit
f9c4e14aec
|
@ -454,9 +454,9 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
except FieldDoesNotExist:
|
except FieldDoesNotExist:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check whether this model is the origin of a M2M relationship
|
# Always allow referencing the primary key since it's already possible
|
||||||
# in which case to_field has to be the pk on this model.
|
# to get this information from the change view URL.
|
||||||
if opts.many_to_many and field.primary_key:
|
if field.primary_key:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Make sure at least one of the models registered for this site
|
# Make sure at least one of the models registered for this site
|
||||||
|
@ -467,8 +467,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
for inline in admin.inlines:
|
for inline in admin.inlines:
|
||||||
registered_models.add(inline.model)
|
registered_models.add(inline.model)
|
||||||
|
|
||||||
for related_object in (opts.get_all_related_objects(include_hidden=True) +
|
for related_object in opts.get_all_related_objects(include_hidden=True):
|
||||||
opts.get_all_related_many_to_many_objects()):
|
|
||||||
related_model = related_object.model
|
related_model = related_object.model
|
||||||
if (any(issubclass(model, related_model) for model in registered_models) and
|
if (any(issubclass(model, related_model) for model in registered_models) and
|
||||||
related_object.field.rel.get_related_field() == field):
|
related_object.field.rel.get_related_field() == field):
|
||||||
|
|
|
@ -4,7 +4,14 @@ Django 1.4.17 release notes
|
||||||
|
|
||||||
*Under development*
|
*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
|
Additionally, Django's vendored version of six, :mod:`django.utils.six`, has
|
||||||
been upgraded to the latest release (1.8.0).
|
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>`_).
|
||||||
|
|
|
@ -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>`_).
|
|
@ -4,7 +4,13 @@ Django 1.6.9 release notes
|
||||||
|
|
||||||
*Under development*
|
*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
|
Additionally, Django's vendored version of six, :mod:`django.utils.six`, has
|
||||||
been upgraded to the latest release (1.8.0).
|
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`).
|
||||||
|
|
|
@ -83,3 +83,6 @@ Bugfixes
|
||||||
|
|
||||||
* Added missing context to the admin's ``delete_selected`` view that prevented
|
* Added missing context to the admin's ``delete_selected`` view that prevented
|
||||||
custom site header, etc. from appearing (:ticket:`23898`).
|
custom site header, etc. from appearing (:ticket:`23898`).
|
||||||
|
|
||||||
|
* Fixed a regression with dynamically generated inlines and allowed field
|
||||||
|
references in the admin (:ticket:`23754`).
|
||||||
|
|
|
@ -57,6 +57,7 @@ versions of the documentation contain the release notes for any later releases.
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
1.5.12
|
||||||
1.5.11
|
1.5.11
|
||||||
1.5.10
|
1.5.10
|
||||||
1.5.9
|
1.5.9
|
||||||
|
|
|
@ -36,8 +36,8 @@ from .models import (Article, Chapter, Child, Parent, Picture, Widget,
|
||||||
FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin,
|
FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin,
|
||||||
State, City, Restaurant, Worker, ParentWithDependentChildren,
|
State, City, Restaurant, Worker, ParentWithDependentChildren,
|
||||||
DependentChild, StumpJoke, FieldOverridePost, FunkyTag,
|
DependentChild, StumpJoke, FieldOverridePost, FunkyTag,
|
||||||
ReferencedByParent, ChildOfReferer, M2MReference, ReferencedByInline,
|
ReferencedByParent, ChildOfReferer, ReferencedByInline,
|
||||||
InlineReference, InlineReferer, Ingredient)
|
InlineReference, InlineReferer, Recipe, Ingredient, NotReferenced)
|
||||||
|
|
||||||
|
|
||||||
def callable_year(dt_value):
|
def callable_year(dt_value):
|
||||||
|
@ -901,7 +901,6 @@ site.register(Worker, WorkerAdmin)
|
||||||
site.register(FunkyTag, FunkyTagAdmin)
|
site.register(FunkyTag, FunkyTagAdmin)
|
||||||
site.register(ReferencedByParent)
|
site.register(ReferencedByParent)
|
||||||
site.register(ChildOfReferer)
|
site.register(ChildOfReferer)
|
||||||
site.register(M2MReference)
|
|
||||||
site.register(ReferencedByInline)
|
site.register(ReferencedByInline)
|
||||||
site.register(InlineReferer, InlineRefererAdmin)
|
site.register(InlineReferer, InlineRefererAdmin)
|
||||||
|
|
||||||
|
@ -940,7 +939,9 @@ site.register(EmptyModelHidden, EmptyModelHiddenAdmin)
|
||||||
site.register(EmptyModelVisible, EmptyModelVisibleAdmin)
|
site.register(EmptyModelVisible, EmptyModelVisibleAdmin)
|
||||||
site.register(EmptyModelMixin, EmptyModelMixinAdmin)
|
site.register(EmptyModelMixin, EmptyModelMixinAdmin)
|
||||||
site.register(StumpJoke)
|
site.register(StumpJoke)
|
||||||
|
site.register(Recipe)
|
||||||
site.register(Ingredient)
|
site.register(Ingredient)
|
||||||
|
site.register(NotReferenced)
|
||||||
|
|
||||||
# Register core models we need in our tests
|
# Register core models we need in our tests
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
|
|
|
@ -827,28 +827,28 @@ class Worker(models.Model):
|
||||||
|
|
||||||
# Models for #23329
|
# Models for #23329
|
||||||
class ReferencedByParent(models.Model):
|
class ReferencedByParent(models.Model):
|
||||||
pass
|
name = models.CharField(max_length=20, unique=True)
|
||||||
|
|
||||||
|
|
||||||
class ParentWithFK(models.Model):
|
class ParentWithFK(models.Model):
|
||||||
fk = models.ForeignKey(ReferencedByParent)
|
fk = models.ForeignKey(
|
||||||
|
ReferencedByParent, to_field='name', related_name='hidden+',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChildOfReferer(ParentWithFK):
|
class ChildOfReferer(ParentWithFK):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class M2MReference(models.Model):
|
|
||||||
ref = models.ManyToManyField('self')
|
|
||||||
|
|
||||||
|
|
||||||
# Models for #23431
|
# Models for #23431
|
||||||
class ReferencedByInline(models.Model):
|
class ReferencedByInline(models.Model):
|
||||||
pass
|
name = models.CharField(max_length=20, unique=True)
|
||||||
|
|
||||||
|
|
||||||
class InlineReference(models.Model):
|
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):
|
class InlineReferer(models.Model):
|
||||||
|
@ -857,9 +857,14 @@ class InlineReferer(models.Model):
|
||||||
|
|
||||||
# Models for #23604
|
# Models for #23604
|
||||||
class Recipe(models.Model):
|
class Recipe(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Ingredient(models.Model):
|
class Ingredient(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
recipes = models.ManyToManyField(Recipe)
|
||||||
recipes = models.ManyToManyField('Recipe', related_name='ingredients')
|
|
||||||
|
|
||||||
|
# Model for #23839
|
||||||
|
class NotReferenced(models.Model):
|
||||||
|
# Don't point any FK at this model.
|
||||||
|
pass
|
||||||
|
|
|
@ -612,26 +612,30 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
self.assertEqual(len(calls), 1)
|
self.assertEqual(len(calls), 1)
|
||||||
|
|
||||||
# Specifying a field referenced by another model should be allowed.
|
# #23839 - Primary key should always be allowed, even if the referenced model isn't registered.
|
||||||
response = self.client.get("/test_admin/admin/admin_views/section/", {TO_FIELD_VAR: 'id'})
|
response = self.client.get("/test_admin/admin/admin_views/notreferenced/", {TO_FIELD_VAR: 'id'})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# Specifying a field referenced by another model though a m2m should be allowed.
|
# 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)
|
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'})
|
response = self.client.get("/test_admin/admin/admin_views/ingredient/", {TO_FIELD_VAR: 'id'})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# #23329 - Specifying a field that is not referred by any other model directly registered
|
# #23329 - Specifying a field that is not referred by any other model directly registered
|
||||||
# to this admin site but registered through inheritance should be allowed.
|
# 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)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# #23431 - Specifying a field that is only referred to by a inline of a registered
|
# #23431 - Specifying a field that is only referred to by a inline of a registered
|
||||||
# model should be allowed.
|
# 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)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# We also want to prevent the add and change view from leaking a
|
# We also want to prevent the add and change view from leaking a
|
||||||
|
|
Loading…
Reference in New Issue