diff --git a/django/db/utils.py b/django/db/utils.py index e442ac1fc1..ccfcfd4dd4 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -326,30 +326,35 @@ class ConnectionRouter(object): method = router.allow_migrate except AttributeError: method = router.allow_syncdb + has_deprecated_signature = True warnings.warn( 'Router.allow_syncdb has been deprecated and will stop working in Django 1.9. ' 'Rename the method to allow_migrate.', RemovedInDjango19Warning, stacklevel=2) + else: + if HAS_INSPECT_SIGNATURE: + sig = inspect.signature(method) + has_deprecated_signature = not any( + p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values() + ) + else: + argspec = inspect.getargspec(method) + has_deprecated_signature = len(argspec.args) == 3 and not argspec.keywords + if has_deprecated_signature: + # Raised here because allow_syncdb has to be called with + # the deprecated signature but shouldn't show this + # warning (only the deprecated method one) + warnings.warn( + "The signature of allow_migrate has changed from " + "allow_migrate(self, db, model) to " + "allow_migrate(self, db, app_label, model_name=None, **hints). " + "Support for the old signature will be removed in Django 1.10.", + RemovedInDjango110Warning) except AttributeError: # If the router doesn't have a method, skip to the next one. continue - if HAS_INSPECT_SIGNATURE: - sig = inspect.signature(router.allow_migrate) - has_deprecated_signature = not any( - p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values() - ) - else: - argspec = inspect.getargspec(router.allow_migrate) - has_deprecated_signature = len(argspec.args) == 3 and not argspec.keywords - if has_deprecated_signature: - warnings.warn( - "The signature of allow_migrate has changed from " - "allow_migrate(self, db, model) to " - "allow_migrate(self, db, app_label, model_name=None, **hints). " - "Support for the old signature will be removed in Django 1.10.", - RemovedInDjango110Warning) model = hints.get('model') allow = None if model is None else method(db, model) else: diff --git a/docs/releases/1.8.7.txt b/docs/releases/1.8.7.txt index 9aed739c01..f3cfc8e046 100644 --- a/docs/releases/1.8.7.txt +++ b/docs/releases/1.8.7.txt @@ -12,4 +12,5 @@ Bugfixes * Fixed a crash of the debug view during the autumn DST change when :setting:`USE_TZ` is ``False`` and ``pytz`` is installed. -* ... +* Fixed a regression in 1.8.6 that caused database routers without an + ``allow_migrate()`` method to crash (:ticket:`25686`). diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index cad410baff..d329340eec 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -956,6 +956,27 @@ class RouterTestCase(TestCase): self.assertTrue(router.allow_migrate('default', 'app_label')) self.assertEqual(force_text(recorded.pop().message), msg) + def test_allow_syncdb_deprecation(self): + class LegacyRouter(object): + def allow_syncdb(self, db, model): + assert db == 'default' + assert model is User + return True + + with override_settings(DATABASE_ROUTERS=[LegacyRouter()]): + with warnings.catch_warnings(record=True) as recorded: + warnings.filterwarnings('always') + msg = ( + "Router.allow_syncdb has been deprecated and will stop " + "working in Django 1.9. Rename the method to allow_migrate." + ) + self.assertTrue(router.allow_migrate_model('default', User)) + self.assertEqual(force_text(recorded.pop().message), msg) + self.assertEqual(recorded, []) + + self.assertTrue(router.allow_migrate('default', 'app_label')) + self.assertEqual(force_text(recorded.pop().message), msg) + def test_partial_router(self): "A router can choose to implement a subset of methods" dive = Book.objects.using('other').create(title="Dive into Python",