1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

[1.8.x] Fixed #24351, #24346 -- Changed the signature of allow_migrate().

The new signature enables better support for routing RunPython and
RunSQL operations, especially w.r.t. reusable and third-party apps.

This commit also takes advantage of the deprecation cycle for the old
signature to remove the backward incompatibility introduced in #22583;
RunPython and RunSQL won't call allow_migrate() when when the router
has the old signature.

Thanks Aymeric Augustin and Tim Graham for helping shape up the patch.

Refs 22583.

Conflicts:
	django/db/utils.py

Backport of bed504d70b from master
This commit is contained in:
Loic Bistuer
2015-02-19 14:27:58 +07:00
parent 564487601e
commit 3a6c37fce4
26 changed files with 222 additions and 118 deletions

View File

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
import datetime
import pickle
import warnings
from operator import attrgetter
from django.contrib.auth.models import User
@@ -11,6 +12,7 @@ from django.db import DEFAULT_DB_ALIAS, connections, router, transaction
from django.db.models import signals
from django.db.utils import ConnectionRouter
from django.test import TestCase, override_settings
from django.utils.encoding import force_text
from django.utils.six import StringIO
from .models import Book, Person, Pet, Review, UserProfile
@@ -901,28 +903,58 @@ class RouterTestCase(TestCase):
def test_migrate_selection(self):
"Synchronization behavior is predictable"
self.assertTrue(router.allow_migrate('default', User))
self.assertTrue(router.allow_migrate('default', Book))
self.assertTrue(router.allow_migrate_model('default', User))
self.assertTrue(router.allow_migrate_model('default', Book))
self.assertTrue(router.allow_migrate('other', User))
self.assertTrue(router.allow_migrate('other', Book))
self.assertTrue(router.allow_migrate_model('other', User))
self.assertTrue(router.allow_migrate_model('other', Book))
with override_settings(DATABASE_ROUTERS=[TestRouter(), AuthRouter()]):
# Add the auth router to the chain. TestRouter is a universal
# synchronizer, so it should have no effect.
self.assertTrue(router.allow_migrate('default', User))
self.assertTrue(router.allow_migrate('default', Book))
self.assertTrue(router.allow_migrate_model('default', User))
self.assertTrue(router.allow_migrate_model('default', Book))
self.assertTrue(router.allow_migrate('other', User))
self.assertTrue(router.allow_migrate('other', Book))
self.assertTrue(router.allow_migrate_model('other', User))
self.assertTrue(router.allow_migrate_model('other', Book))
with override_settings(DATABASE_ROUTERS=[AuthRouter(), TestRouter()]):
# Now check what happens if the router order is reversed.
self.assertFalse(router.allow_migrate('default', User))
self.assertTrue(router.allow_migrate('default', Book))
self.assertFalse(router.allow_migrate_model('default', User))
self.assertTrue(router.allow_migrate_model('default', Book))
self.assertTrue(router.allow_migrate('other', User))
self.assertFalse(router.allow_migrate('other', Book))
self.assertTrue(router.allow_migrate_model('other', User))
self.assertTrue(router.allow_migrate_model('other', Book))
def test_migrate_legacy_router(self):
class LegacyRouter(object):
def allow_migrate(self, db, model):
"""
Deprecated allow_migrate signature should trigger
RemovedInDjango20Warning.
"""
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 = (
"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 2.0."
)
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"
@@ -939,8 +971,8 @@ class RouterTestCase(TestCase):
self.assertTrue(router.allow_relation(dive, dive))
self.assertTrue(router.allow_migrate('default', User))
self.assertTrue(router.allow_migrate('default', Book))
self.assertTrue(router.allow_migrate_model('default', User))
self.assertTrue(router.allow_migrate_model('default', Book))
with override_settings(DATABASE_ROUTERS=[WriteRouter(), AuthRouter(), TestRouter()]):
self.assertEqual(router.db_for_read(User), 'default')
@@ -951,8 +983,8 @@ class RouterTestCase(TestCase):
self.assertTrue(router.allow_relation(dive, dive))
self.assertFalse(router.allow_migrate('default', User))
self.assertTrue(router.allow_migrate('default', Book))
self.assertFalse(router.allow_migrate_model('default', User))
self.assertTrue(router.allow_migrate_model('default', Book))
def test_database_routing(self):
marty = Person.objects.using('default').create(name="Marty Alchin")
@@ -1492,11 +1524,11 @@ class AntiPetRouter(object):
# A router that only expresses an opinion on migrate,
# passing pets to the 'other' database
def allow_migrate(self, db, model):
def allow_migrate(self, db, app_label, model_name, **hints):
if db == 'other':
return model._meta.object_name == 'Pet'
return model_name == 'pet'
else:
return model._meta.object_name != 'Pet'
return model_name != 'pet'
class FixtureTestCase(TestCase):
@@ -1757,7 +1789,7 @@ class RouterModelArgumentTestCase(TestCase):
class SyncOnlyDefaultDatabaseRouter(object):
def allow_migrate(self, db, model):
def allow_migrate(self, db, app_label, **hints):
return db == DEFAULT_DB_ALIAS