Fixed #24146 -- Fixed a missing fields regression in admin checks.
This allows using get_field() early in the app loading process. Thanks to PirosB3 and Tim Graham.
This commit is contained in:
parent
8e8daf7c9b
commit
e8171daf0c
|
@ -487,6 +487,12 @@ class Options(object):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def fields_map(self):
|
def fields_map(self):
|
||||||
|
return self._get_fields_map()
|
||||||
|
|
||||||
|
def _get_fields_map(self):
|
||||||
|
# Helper method to provide a way to access this without caching it.
|
||||||
|
# For example, admin checks run before the app cache is ready and we
|
||||||
|
# need to be able to lookup fields before we cache the final result.
|
||||||
res = {}
|
res = {}
|
||||||
fields = self._get_fields(forward=False, include_hidden=True)
|
fields = self._get_fields(forward=False, include_hidden=True)
|
||||||
for field in fields:
|
for field in fields:
|
||||||
|
@ -531,20 +537,26 @@ class Options(object):
|
||||||
|
|
||||||
return field
|
return field
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If the app registry is not ready, reverse fields are
|
pass
|
||||||
# unavailable, therefore we throw a FieldDoesNotExist exception.
|
|
||||||
if not self.apps.ready:
|
|
||||||
raise FieldDoesNotExist(
|
|
||||||
"%s has no field named %r. The app cache isn't ready yet, "
|
|
||||||
"so if this is an auto-created related field, it won't "
|
|
||||||
"be available yet." % (self.object_name, field_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if m2m_in_kwargs:
|
if m2m_in_kwargs:
|
||||||
# Previous API does not allow searching reverse fields.
|
# Previous API does not allow searching reverse fields.
|
||||||
raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name))
|
raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name))
|
||||||
|
|
||||||
|
# If the app registry is not ready, reverse fields are probably
|
||||||
|
# unavailable, but try anyway.
|
||||||
|
if not self.apps.ready:
|
||||||
|
try:
|
||||||
|
# Don't cache results
|
||||||
|
return self._get_fields_map()[field_name]
|
||||||
|
except KeyError:
|
||||||
|
raise FieldDoesNotExist(
|
||||||
|
"%s has no field named %r. The app cache isn't ready yet, "
|
||||||
|
"so if this is an auto-created related field, it might not "
|
||||||
|
"be available yet." % (self.object_name, field_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
# Retrieve field instance by name from cached or just-computed
|
# Retrieve field instance by name from cached or just-computed
|
||||||
# field map.
|
# field map.
|
||||||
return self.fields_map[field_name]
|
return self.fields_map[field_name]
|
||||||
|
|
|
@ -169,24 +169,30 @@ class GetFieldByNameTests(OptionsBaseTests):
|
||||||
self.assertEqual(field_info[1:], (None, True, False))
|
self.assertEqual(field_info[1:], (None, True, False))
|
||||||
self.assertIsInstance(field_info[0], GenericRelation)
|
self.assertIsInstance(field_info[0], GenericRelation)
|
||||||
|
|
||||||
def test_get_fields_only_searches_forward_on_apps_not_ready(self):
|
def test_get_fields_when_apps_not_ready(self):
|
||||||
opts = Person._meta
|
opts = Person._meta
|
||||||
# If apps registry is not ready, get_field() searches over only
|
# If apps registry is not ready, get_field() searches over only
|
||||||
# forward fields.
|
# forward fields.
|
||||||
opts.apps.ready = False
|
opts.apps.ready = False
|
||||||
|
# Clear cached data.
|
||||||
|
opts.__dict__.pop('fields_map', None)
|
||||||
try:
|
try:
|
||||||
# 'data_abstract' is a forward field, and therefore will be found
|
# 'data_abstract' is a forward field, and therefore will be found
|
||||||
self.assertTrue(opts.get_field('data_abstract'))
|
self.assertTrue(opts.get_field('data_abstract'))
|
||||||
msg = (
|
msg = (
|
||||||
"Person has no field named 'relating_baseperson'. The app "
|
"Person has no field named 'some_missing_field'. The app "
|
||||||
"cache isn't ready yet, so if this is an auto-created related "
|
"cache isn't ready yet, so if this is an auto-created related "
|
||||||
"field, it won't be available yet."
|
"field, it might not be available yet."
|
||||||
)
|
)
|
||||||
# 'data_abstract' is a reverse field, and will raise an exception
|
|
||||||
with self.assertRaisesMessage(FieldDoesNotExist, msg):
|
with self.assertRaisesMessage(FieldDoesNotExist, msg):
|
||||||
opts.get_field('relating_baseperson')
|
opts.get_field('some_missing_field')
|
||||||
|
# Be sure it's not cached
|
||||||
|
self.assertNotIn('fields_map', opts.__dict__)
|
||||||
finally:
|
finally:
|
||||||
opts.apps.ready = True
|
opts.apps.ready = True
|
||||||
|
# At this point searching a related field would cache fields_map
|
||||||
|
opts.get_field('relating_baseperson')
|
||||||
|
self.assertIn('fields_map', opts.__dict__)
|
||||||
|
|
||||||
|
|
||||||
class RelationTreeTests(TestCase):
|
class RelationTreeTests(TestCase):
|
||||||
|
|
Loading…
Reference in New Issue