Commit 9cd5201a authored by Anssi Kääriäinen's avatar Anssi Kääriäinen Committed by Tim Graham
Browse files

Fixed #22994 -- regression with generic FK + admin list_view

The reason for the regression was that the GenericForeignKey field isn't
something meta.get_field_by_name() should return. The reason is that a
couple of places in Django expects get_field_by_name() to work this way.
It could make sense to return GFKs from get_field_by_name(), but that
should likely be done as part of meta refactoring or virtual fields
refactoring patches.

Thanks to glicerinu@gmail.com for the report and to Tim for working on
the issue.
parent 38e001ab
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -441,7 +441,9 @@ class Options(object):
        for f, model in self.get_fields_with_model():
            cache[f.name] = cache[f.attname] = (f, model, True, False)
        for f in self.virtual_fields:
            cache[f.name] = (f, None if f.model == self.model else f.model, True, False)
            if hasattr(f, 'related'):
                cache[f.name] = cache[f.attname] = (
                    f, None if f.model == self.model else f.model, True, False)
        if apps.ready:
            self._name_map = cache
        return cache
+6 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ from .models import (Article, Chapter, Child, Parent, Picture, Widget,
    UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram,
    FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin,
    State, City, Restaurant, Worker, ParentWithDependentChildren,
    DependentChild, StumpJoke, FieldOverridePost)
    DependentChild, StumpJoke, FieldOverridePost, FunkyTag)


def callable_year(dt_value):
@@ -827,6 +827,10 @@ class RestaurantAdmin(admin.ModelAdmin):
        return {'name': 'overridden_value'}


class FunkyTagAdmin(admin.ModelAdmin):
    list_display = ('name', 'content_object')


site = admin.AdminSite(name="admin")
site.register(Article, ArticleAdmin)
site.register(CustomArticle, CustomArticleAdmin)
@@ -882,6 +886,7 @@ site.register(State, StateAdmin)
site.register(City, CityAdmin)
site.register(Restaurant, RestaurantAdmin)
site.register(Worker, WorkerAdmin)
site.register(FunkyTag, FunkyTagAdmin)

# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:
+16 −1
Original line number Diff line number Diff line
@@ -1718,11 +1718,26 @@ class AdminViewDeletedObjectsTest(TestCase):
        """
        plot = Plot.objects.get(pk=3)
        FunkyTag.objects.create(content_object=plot, name='hott')
        should_contain = """<li>Funky tag: hott"""
        should_contain = """<li>Funky tag: <a href="/test_admin/admin/admin_views/funkytag/1/">hott"""
        response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(3))
        self.assertContains(response, should_contain)


@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
    ROOT_URLCONF="admin_views.urls")
class TestGenericRelations(TestCase):
    fixtures = ['admin-views-users.xml', 'deleted-objects.xml']

    def setUp(self):
        self.client.login(username='super', password='secret')

    def test_generic_content_object_in_list_display(self):
        plot = Plot.objects.get(pk=3)
        FunkyTag.objects.create(content_object=plot, name='hott')
        response = self.client.get('/test_admin/admin/admin_views/funkytag/')
        self.assertContains(response, "%s</td>" % plot)


@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
    ROOT_URLCONF="admin_views.urls")
class AdminViewStringPrimaryKeyTest(TestCase):
+10 −5
Original line number Diff line number Diff line
from django import test

from django.db.models.fields import related, CharField, Field
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db.models.fields import related, CharField, Field, FieldDoesNotExist
from django.contrib.contenttypes.fields import GenericRelation

from .models import (
    AbstractPerson, BasePerson, Person, Relating, Relation
@@ -650,7 +650,12 @@ class GetFieldByNameTests(OptionsBaseTests):
        self.assertEqual(field_info[1:], (None, False, True))
        self.assertIsInstance(field_info[0], related.RelatedObject)

    def test_get_virtual_field(self):
        field_info = Person._meta.get_field_by_name('content_object_base')
    def test_get_generic_foreign_key(self):
        # For historic reasons generic foreign keys aren't available.
        with self.assertRaises(FieldDoesNotExist):
            Person._meta.get_field_by_name('content_object_base')

    def test_get_generic_relation(self):
        field_info = Person._meta.get_field_by_name('generic_relation_base')
        self.assertEqual(field_info[1:], (None, True, False))
        self.assertIsInstance(field_info[0], GenericForeignKey)
        self.assertIsInstance(field_info[0], GenericRelation)