1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

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.
This commit is contained in:
Anssi Kääriäinen 2014-07-14 14:23:34 +03:00 committed by Tim Graham
parent 38e001ab6c
commit 9cd5201abd
4 changed files with 35 additions and 8 deletions

View File

@ -441,7 +441,9 @@ class Options(object):
for f, model in self.get_fields_with_model(): for f, model in self.get_fields_with_model():
cache[f.name] = cache[f.attname] = (f, model, True, False) cache[f.name] = cache[f.attname] = (f, model, True, False)
for f in self.virtual_fields: 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: if apps.ready:
self._name_map = cache self._name_map = cache
return cache return cache

View File

@ -35,7 +35,7 @@ from .models import (Article, Chapter, Child, Parent, Picture, Widget,
UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram, UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram,
FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin, FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin,
State, City, Restaurant, Worker, ParentWithDependentChildren, State, City, Restaurant, Worker, ParentWithDependentChildren,
DependentChild, StumpJoke, FieldOverridePost) DependentChild, StumpJoke, FieldOverridePost, FunkyTag)
def callable_year(dt_value): def callable_year(dt_value):
@ -827,6 +827,10 @@ class RestaurantAdmin(admin.ModelAdmin):
return {'name': 'overridden_value'} return {'name': 'overridden_value'}
class FunkyTagAdmin(admin.ModelAdmin):
list_display = ('name', 'content_object')
site = admin.AdminSite(name="admin") site = admin.AdminSite(name="admin")
site.register(Article, ArticleAdmin) site.register(Article, ArticleAdmin)
site.register(CustomArticle, CustomArticleAdmin) site.register(CustomArticle, CustomArticleAdmin)
@ -882,6 +886,7 @@ site.register(State, StateAdmin)
site.register(City, CityAdmin) site.register(City, CityAdmin)
site.register(Restaurant, RestaurantAdmin) site.register(Restaurant, RestaurantAdmin)
site.register(Worker, WorkerAdmin) site.register(Worker, WorkerAdmin)
site.register(FunkyTag, FunkyTagAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases: # That way we cover all four cases:

View File

@ -1718,11 +1718,26 @@ class AdminViewDeletedObjectsTest(TestCase):
""" """
plot = Plot.objects.get(pk=3) plot = Plot.objects.get(pk=3)
FunkyTag.objects.create(content_object=plot, name='hott') 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)) response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(3))
self.assertContains(response, should_contain) 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',), @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
ROOT_URLCONF="admin_views.urls") ROOT_URLCONF="admin_views.urls")
class AdminViewStringPrimaryKeyTest(TestCase): class AdminViewStringPrimaryKeyTest(TestCase):

View File

@ -1,7 +1,7 @@
from django import test from django import test
from django.db.models.fields import related, CharField, Field from django.db.models.fields import related, CharField, Field, FieldDoesNotExist
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericRelation
from .models import ( from .models import (
AbstractPerson, BasePerson, Person, Relating, Relation AbstractPerson, BasePerson, Person, Relating, Relation
@ -650,7 +650,12 @@ class GetFieldByNameTests(OptionsBaseTests):
self.assertEqual(field_info[1:], (None, False, True)) self.assertEqual(field_info[1:], (None, False, True))
self.assertIsInstance(field_info[0], related.RelatedObject) self.assertIsInstance(field_info[0], related.RelatedObject)
def test_get_virtual_field(self): def test_get_generic_foreign_key(self):
field_info = Person._meta.get_field_by_name('content_object_base') # 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.assertEqual(field_info[1:], (None, True, False))
self.assertIsInstance(field_info[0], GenericForeignKey) self.assertIsInstance(field_info[0], GenericRelation)