Skip to content
Permalink
Browse files

[1.5.x] Fixed #23754 -- Always allowed reference to the primary key i…

…n 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 f9c4e14 from master
  • Loading branch information...
charettes authored and timgraham committed Nov 16, 2014
1 parent 17ffd24 commit 3d35ea43001991d94c192abca1832628cd255bb0
@@ -287,9 +287,9 @@ def to_field_allowed(self, request, to_field):
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
@@ -300,8 +300,7 @@ def to_field_allowed(self, request, to_field):
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):
@@ -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>`_).
@@ -25,6 +25,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
@@ -29,7 +29,7 @@
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):
@@ -714,7 +714,6 @@ class InlineRefererAdmin(admin.ModelAdmin):
site.register(UndeletableObject, UndeletableObjectAdmin)
site.register(ReferencedByParent)
site.register(ChildOfReferer)
site.register(M2MReference)
site.register(ReferencedByInline)
site.register(InlineReferer, InlineRefererAdmin)

@@ -746,7 +745,9 @@ class InlineRefererAdmin(admin.ModelAdmin):
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
@@ -682,11 +682,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):
@@ -696,13 +698,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):
@@ -711,9 +716,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
@@ -567,26 +567,30 @@ def test_disallowed_to_field(self):
with self.assertRaises(DisallowedModelAdminToField):
response = self.client.get("/test_admin/admin/admin_views/section/", {TO_FIELD_VAR: 'name'})

# 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):

0 comments on commit 3d35ea4

Please sign in to comment.
You can’t perform that action at this time.