[1.9.x] Fixed #25622 -- Accounted for generic relations in the admin to field validation

Thanks to Jonathan Liuti for the report and Tim Graham for the review.

Backport of 9dcfecb7c6 from master
This commit is contained in:
Simon Charette 2015-10-28 11:25:25 -04:00
parent 6bb9f51ab8
commit 6eaf43a244
5 changed files with 40 additions and 13 deletions

View File

@ -413,8 +413,10 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
)
for related_object in related_objects:
related_model = related_object.related_model
remote_field = related_object.field.remote_field
if (any(issubclass(model, related_model) for model in registered_models) and
related_object.field.remote_field.get_related_field() == field):
hasattr(remote_field, 'get_related_field') and
remote_field.get_related_field() == field):
return True
return False

View File

@ -48,3 +48,6 @@ Bugfixes
* Fixed a regression in ``URLValidator`` that allowed URLs with consecutive
dots in the domain section (like ``http://example..com/``) to pass
(:ticket:`25620`).
* Fixed a crash with ``GenericRelation`` and
``BaseModelAdmin.to_field_allowed`` (:ticket:`25622`).

View File

@ -31,18 +31,19 @@ from .models import (
EmptyModelHidden, EmptyModelMixin, EmptyModelVisible, ExplicitlyProvidedPK,
ExternalSubscriber, Fabric, FancyDoodad, FieldOverridePost,
FilteredManager, FooAccount, FoodDelivery, FunkyTag, Gadget, Gallery,
Grommet, ImplicitlyGeneratedPK, Ingredient, InlineReference, InlineReferer,
Inquisition, Language, Link, MainPrepopulated, ModelWithStringPrimaryKey,
NotReferenced, OldSubscriber, OtherStory, Paper, Parent,
ParentWithDependentChildren, Person, Persona, Picture, Pizza, Plot,
PlotDetails, PluggableSearchPerson, Podcast, Post, PrePopulatedPost,
PrePopulatedPostLargeSlug, PrePopulatedSubPost, Promo, Question, Recipe,
Recommendation, Recommender, ReferencedByInline, ReferencedByParent,
RelatedPrepopulated, Report, Reservation, Restaurant,
RowLevelChangePermissionModel, Section, ShortMessage, Simple, Sketch,
State, Story, StumpJoke, Subscriber, SuperVillain, Telegram, Thing,
Topping, UnchangeableObject, UndeletableObject, UnorderedObject,
UserMessenger, Villain, Vodcast, Whatsit, Widget, Worker, WorkHour,
GenRelReference, Grommet, ImplicitlyGeneratedPK, Ingredient,
InlineReference, InlineReferer, Inquisition, Language, Link,
MainPrepopulated, ModelWithStringPrimaryKey, NotReferenced, OldSubscriber,
OtherStory, Paper, Parent, ParentWithDependentChildren, Person, Persona,
Picture, Pizza, Plot, PlotDetails, PluggableSearchPerson, Podcast, Post,
PrePopulatedPost, PrePopulatedPostLargeSlug, PrePopulatedSubPost, Promo,
Question, Recipe, Recommendation, Recommender, ReferencedByGenRel,
ReferencedByInline, ReferencedByParent, RelatedPrepopulated, Report,
Reservation, Restaurant, RowLevelChangePermissionModel, Section,
ShortMessage, Simple, Sketch, State, Story, StumpJoke, Subscriber,
SuperVillain, Telegram, Thing, Topping, UnchangeableObject,
UndeletableObject, UnorderedObject, UserMessenger, Villain, Vodcast,
Whatsit, Widget, Worker, WorkHour,
)
@ -944,6 +945,8 @@ site.register(ReferencedByParent)
site.register(ChildOfReferer)
site.register(ReferencedByInline)
site.register(InlineReferer, InlineRefererAdmin)
site.register(ReferencedByGenRel)
site.register(GenRelReference)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:

View File

@ -938,3 +938,14 @@ class ExplicitlyProvidedPK(models.Model):
class ImplicitlyGeneratedPK(models.Model):
name = models.IntegerField(unique=True)
# Models for #25622
class ReferencedByGenRel(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class GenRelReference(models.Model):
references = GenericRelation(ReferencedByGenRel)

View File

@ -725,6 +725,14 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
response = self.client.get(reverse('admin:admin_views_referencedbyinline_changelist'), {TO_FIELD_VAR: 'name'})
self.assertEqual(response.status_code, 200)
# #25622 - Specifying a field of a model only referred by a generic
# relation should raise DisallowedModelAdminToField.
url = reverse('admin:admin_views_referencedbygenrel_changelist')
with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls:
response = self.client.get(url, {TO_FIELD_VAR: 'object_id'})
self.assertEqual(response.status_code, 400)
self.assertEqual(len(calls), 1)
# We also want to prevent the add, change, and delete views from
# leaking a disallowed field value.
with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls: