diff options
| author | Loic Bistuer <loic.bistuer@gmail.com> | 2014-10-22 23:16:32 +0700 |
|---|---|---|
| committer | Loic Bistuer <loic.bistuer@gmail.com> | 2014-10-26 00:12:17 +0700 |
| commit | 7fa6781f818deea61cef4c13b139fa9586a6ca7a (patch) | |
| tree | e1b1909276317b6a5ff968f1bcaa96d2ed0a3e38 | |
| parent | 7227ec8eccde8ea28addad6c276276cb1d2a6efe (diff) | |
[1.7.x] 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.
Backport of 8c4ca16c65 and b62f72498a from master
| -rw-r--r-- | django/apps/registry.py | 14 | ||||
| -rw-r--r-- | docs/releases/1.7.2.txt | 4 | ||||
| -rw-r--r-- | tests/apps/tests.py | 39 |
3 files changed, 54 insertions, 3 deletions
diff --git a/django/apps/registry.py b/django/apps/registry.py index 57bdc3edb2..fe53d965de 100644 --- a/django/apps/registry.py +++ b/django/apps/registry.py @@ -208,9 +208,17 @@ class Apps(object): model_name = model._meta.model_name app_models = self.all_models[app_label] if model_name in app_models: - raise RuntimeError( - "Conflicting '%s' models in application '%s': %s and %s." % - (model_name, app_label, app_models[model_name], model)) + if (model.__name__ == app_models[model_name].__name__ and + model.__module__ == app_models[model_name].__module__): + warnings.warn( + "Model '%s.%s' was already registered. " + "Reloading models is not advised as it can lead to inconsistencies, " + "most notably with related models." % (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 self.clear_cache() diff --git a/docs/releases/1.7.2.txt b/docs/releases/1.7.2.txt index 32500045f0..c25aafba1d 100644 --- a/docs/releases/1.7.2.txt +++ b/docs/releases/1.7.2.txt @@ -14,3 +14,7 @@ Bugfixes * Fixed a migration crash when adding an explicit ``id`` field to a model on SQLite (:ticket:`23702`). + +* Warn for duplicate models when a module is reloaded. Previously a +RuntimeError was raised every time two models clashed in the app registry. +(:ticket:`23621`). diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 513ed32039..1a308efb6a 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import os import sys from unittest import skipUnless +import warnings from django.apps import apps, AppConfig from django.apps.registry import Apps @@ -208,6 +209,44 @@ class AppsTests(TestCase): apps.get_model("apps", "SouthPonies") 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. " + "Reloading models is not advised as it can lead to inconsistencies, " + "most notably with related models.") + + # 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): def __init__(self, **kwargs): |
