Loading django/db/models/options.py +20 −8 Original line number Diff line number Diff line Loading @@ -487,6 +487,12 @@ class Options(object): @cached_property 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 = {} fields = self._get_fields(forward=False, include_hidden=True) for field in fields: Loading Loading @@ -531,20 +537,26 @@ class Options(object): return field except KeyError: # If the app registry is not ready, reverse fields are # unavailable, therefore we throw a FieldDoesNotExist exception. pass if m2m_in_kwargs: # Previous API does not allow searching reverse fields. 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 won't " "so if this is an auto-created related field, it might not " "be available yet." % (self.object_name, field_name) ) try: if m2m_in_kwargs: # Previous API does not allow searching reverse fields. raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) # Retrieve field instance by name from cached or just-computed # field map. return self.fields_map[field_name] Loading tests/model_meta/tests.py +11 −5 Original line number Diff line number Diff line Loading @@ -169,24 +169,30 @@ class GetFieldByNameTests(OptionsBaseTests): self.assertEqual(field_info[1:], (None, True, False)) 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 # If apps registry is not ready, get_field() searches over only # forward fields. opts.apps.ready = False # Clear cached data. opts.__dict__.pop('fields_map', None) try: # 'data_abstract' is a forward field, and therefore will be found self.assertTrue(opts.get_field('data_abstract')) 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 " "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): opts.get_field('relating_baseperson') opts.get_field('some_missing_field') # Be sure it's not cached self.assertNotIn('fields_map', opts.__dict__) finally: 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): Loading Loading
django/db/models/options.py +20 −8 Original line number Diff line number Diff line Loading @@ -487,6 +487,12 @@ class Options(object): @cached_property 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 = {} fields = self._get_fields(forward=False, include_hidden=True) for field in fields: Loading Loading @@ -531,20 +537,26 @@ class Options(object): return field except KeyError: # If the app registry is not ready, reverse fields are # unavailable, therefore we throw a FieldDoesNotExist exception. pass if m2m_in_kwargs: # Previous API does not allow searching reverse fields. 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 won't " "so if this is an auto-created related field, it might not " "be available yet." % (self.object_name, field_name) ) try: if m2m_in_kwargs: # Previous API does not allow searching reverse fields. raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) # Retrieve field instance by name from cached or just-computed # field map. return self.fields_map[field_name] Loading
tests/model_meta/tests.py +11 −5 Original line number Diff line number Diff line Loading @@ -169,24 +169,30 @@ class GetFieldByNameTests(OptionsBaseTests): self.assertEqual(field_info[1:], (None, True, False)) 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 # If apps registry is not ready, get_field() searches over only # forward fields. opts.apps.ready = False # Clear cached data. opts.__dict__.pop('fields_map', None) try: # 'data_abstract' is a forward field, and therefore will be found self.assertTrue(opts.get_field('data_abstract')) 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 " "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): opts.get_field('relating_baseperson') opts.get_field('some_missing_field') # Be sure it's not cached self.assertNotIn('fields_map', opts.__dict__) finally: 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): Loading