mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #23621 -- Warn for duplicate models when a module is reloaded.
Previously a RuntimeError was raised every time two models clashed in the app registry. This prevented reloading a module in a REPL; while it's not recommended to do so, we decided not to forbid this use-case by turning the error into a warning. Thanks @dfunckt and Sergey Pashinin for the initial patches.
This commit is contained in:
		| @@ -208,9 +208,15 @@ class Apps(object): | |||||||
|         model_name = model._meta.model_name |         model_name = model._meta.model_name | ||||||
|         app_models = self.all_models[app_label] |         app_models = self.all_models[app_label] | ||||||
|         if model_name in app_models: |         if model_name in app_models: | ||||||
|             raise RuntimeError( |             if (model.__name__ == app_models[model_name].__name__ and | ||||||
|                 "Conflicting '%s' models in application '%s': %s and %s." % |                     model.__module__ == app_models[model_name].__module__): | ||||||
|                 (model_name, app_label, app_models[model_name], model)) |                 warnings.warn( | ||||||
|  |                     "Model '%s.%s' was already registered." % (model_name, app_label), | ||||||
|  |                     RuntimeWarning, stacklevel=2) | ||||||
|  |             else: | ||||||
|  |                 raise RuntimeError( | ||||||
|  |                     "Conflicting '%s' models in application '%s': %s and %s." % | ||||||
|  |                     (model_name, app_label, app_models[model_name], model)) | ||||||
|         app_models[model_name] = model |         app_models[model_name] = model | ||||||
|         self.clear_cache() |         self.clear_cache() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ from __future__ import unicode_literals | |||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| from unittest import skipUnless | from unittest import skipUnless | ||||||
|  | import warnings | ||||||
|  |  | ||||||
| from django.apps import apps, AppConfig | from django.apps import apps, AppConfig | ||||||
| from django.apps.registry import Apps | from django.apps.registry import Apps | ||||||
| @@ -208,6 +209,41 @@ class AppsTests(TestCase): | |||||||
|             apps.get_model("apps", "SouthPonies") |             apps.get_model("apps", "SouthPonies") | ||||||
|         self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model) |         self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model) | ||||||
|  |  | ||||||
|  |     def test_model_clash(self): | ||||||
|  |         """ | ||||||
|  |         Test for behavior when two models clash in the app registry. | ||||||
|  |         """ | ||||||
|  |         new_apps = Apps(["apps"]) | ||||||
|  |         meta_contents = { | ||||||
|  |             'app_label': "apps", | ||||||
|  |             'apps': new_apps, | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         body = {} | ||||||
|  |         body['Meta'] = type(str("Meta"), tuple(), meta_contents) | ||||||
|  |         body['__module__'] = TotallyNormal.__module__ | ||||||
|  |         type(str("SouthPonies"), (models.Model,), body) | ||||||
|  |  | ||||||
|  |         # When __name__ and __module__ match we assume the module | ||||||
|  |         # was reloaded and issue a warning. This use-case is | ||||||
|  |         # useful for REPL. Refs #23621. | ||||||
|  |         body = {} | ||||||
|  |         body['Meta'] = type(str("Meta"), tuple(), meta_contents) | ||||||
|  |         body['__module__'] = TotallyNormal.__module__ | ||||||
|  |         with warnings.catch_warnings(record=True) as w: | ||||||
|  |             type(str("SouthPonies"), (models.Model,), body) | ||||||
|  |             self.assertEqual(len(w), 1) | ||||||
|  |             self.assertTrue(issubclass(w[-1].category, RuntimeWarning)) | ||||||
|  |             self.assertEqual(str(w[-1].message), "Model 'southponies.apps' was already registered.") | ||||||
|  |  | ||||||
|  |         # If it doesn't appear to be a reloaded module then we expect | ||||||
|  |         # a RuntimeError. | ||||||
|  |         body = {} | ||||||
|  |         body['Meta'] = type(str("Meta"), tuple(), meta_contents) | ||||||
|  |         body['__module__'] = TotallyNormal.__module__ + '.whatever' | ||||||
|  |         with six.assertRaisesRegex(self, RuntimeError, | ||||||
|  |                 "Conflicting 'southponies' models in application 'apps':.*"): | ||||||
|  |             type(str("SouthPonies"), (models.Model,), body) | ||||||
|  |  | ||||||
| class Stub(object): | class Stub(object): | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user