Commit 6eaf43a2 authored by Simon Charette's avatar Simon Charette
Browse files

[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 9dcfecb7 from master
parent 6bb9f51a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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`).
+15 −12
Original line number Diff line number Diff line
@@ -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:
+11 −0
Original line number Diff line number Diff line
@@ -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)
+8 −0
Original line number Diff line number Diff line
@@ -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: