Merge pull request #2692 from fcurella/patch-5
#22667 replaced occurrences of master/slave terminology with leader/follower
This commit is contained in:
commit
8a95b4fca7
|
@ -649,10 +649,10 @@ Default: ``None``
|
||||||
The alias of the database that this database should mirror during
|
The alias of the database that this database should mirror during
|
||||||
testing.
|
testing.
|
||||||
|
|
||||||
This setting exists to allow for testing of master/slave
|
This setting exists to allow for testing of leader/follower
|
||||||
configurations of multiple databases. See the documentation on
|
configurations of multiple databases. See the documentation on
|
||||||
:ref:`testing master/slave configurations
|
:ref:`testing leader/follower configurations
|
||||||
<topics-testing-masterslave>` for details.
|
<topics-testing-leaderfollower>` for details.
|
||||||
|
|
||||||
.. setting:: TEST_NAME
|
.. setting:: TEST_NAME
|
||||||
|
|
||||||
|
|
|
@ -222,29 +222,29 @@ won't appear in the models cache, but the model details can be used
|
||||||
for routing purposes.
|
for routing purposes.
|
||||||
|
|
||||||
For example, the following router would direct all cache read
|
For example, the following router would direct all cache read
|
||||||
operations to ``cache_slave``, and all write operations to
|
operations to ``cache_follower``, and all write operations to
|
||||||
``cache_master``. The cache table will only be synchronized onto
|
``cache_leader``. The cache table will only be synchronized onto
|
||||||
``cache_master``::
|
``cache_leader``::
|
||||||
|
|
||||||
class CacheRouter(object):
|
class CacheRouter(object):
|
||||||
"""A router to control all database cache operations"""
|
"""A router to control all database cache operations"""
|
||||||
|
|
||||||
def db_for_read(self, model, **hints):
|
def db_for_read(self, model, **hints):
|
||||||
"All cache read operations go to the slave"
|
"All cache read operations go to the follower"
|
||||||
if model._meta.app_label in ('django_cache',):
|
if model._meta.app_label in ('django_cache',):
|
||||||
return 'cache_slave'
|
return 'cache_follower'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def db_for_write(self, model, **hints):
|
def db_for_write(self, model, **hints):
|
||||||
"All cache write operations go to master"
|
"All cache write operations go to leader"
|
||||||
if model._meta.app_label in ('django_cache',):
|
if model._meta.app_label in ('django_cache',):
|
||||||
return 'cache_master'
|
return 'cache_leader'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def allow_migrate(self, db, model):
|
def allow_migrate(self, db, model):
|
||||||
"Only install the cache model on master"
|
"Only install the cache model on leader"
|
||||||
if model._meta.app_label in ('django_cache',):
|
if model._meta.app_label in ('django_cache',):
|
||||||
return db == 'cache_master'
|
return db == 'cache_leader'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
If you don't specify routing directions for the database cache model,
|
If you don't specify routing directions for the database cache model,
|
||||||
|
|
|
@ -197,17 +197,17 @@ Using routers
|
||||||
|
|
||||||
Database routers are installed using the :setting:`DATABASE_ROUTERS`
|
Database routers are installed using the :setting:`DATABASE_ROUTERS`
|
||||||
setting. This setting defines a list of class names, each specifying a
|
setting. This setting defines a list of class names, each specifying a
|
||||||
router that should be used by the master router
|
router that should be used by the leader router
|
||||||
(``django.db.router``).
|
(``django.db.router``).
|
||||||
|
|
||||||
The master router is used by Django's database operations to allocate
|
The leader router is used by Django's database operations to allocate
|
||||||
database usage. Whenever a query needs to know which database to use,
|
database usage. Whenever a query needs to know which database to use,
|
||||||
it calls the master router, providing a model and a hint (if
|
it calls the leader router, providing a model and a hint (if
|
||||||
available). Django then tries each router in turn until a database
|
available). Django then tries each router in turn until a database
|
||||||
suggestion can be found. If no suggestion can be found, it tries the
|
suggestion can be found. If no suggestion can be found, it tries the
|
||||||
current ``_state.db`` of the hint instance. If a hint instance wasn't
|
current ``_state.db`` of the hint instance. If a hint instance wasn't
|
||||||
provided, or the instance doesn't currently have database state, the
|
provided, or the instance doesn't currently have database state, the
|
||||||
master router will allocate the ``default`` database.
|
leader router will allocate the ``default`` database.
|
||||||
|
|
||||||
An example
|
An example
|
||||||
----------
|
----------
|
||||||
|
@ -225,16 +225,16 @@ An example
|
||||||
introduce referential integrity problems that Django can't
|
introduce referential integrity problems that Django can't
|
||||||
currently handle.
|
currently handle.
|
||||||
|
|
||||||
The master/slave configuration described is also flawed -- it
|
The leader/follower configuration described is also flawed -- it
|
||||||
doesn't provide any solution for handling replication lag (i.e.,
|
doesn't provide any solution for handling replication lag (i.e.,
|
||||||
query inconsistencies introduced because of the time taken for a
|
query inconsistencies introduced because of the time taken for a
|
||||||
write to propagate to the slaves). It also doesn't consider the
|
write to propagate to the followers). It also doesn't consider the
|
||||||
interaction of transactions with the database utilization strategy.
|
interaction of transactions with the database utilization strategy.
|
||||||
|
|
||||||
So - what does this mean in practice? Let's consider another sample
|
So - what does this mean in practice? Let's consider another sample
|
||||||
configuration. This one will have several databases: one for the
|
configuration. This one will have several databases: one for the
|
||||||
``auth`` application, and all other apps using a master/slave setup
|
``auth`` application, and all other apps using a leader/follower setup
|
||||||
with two read slaves. Here are the settings specifying these
|
with two read followers. Here are the settings specifying these
|
||||||
databases::
|
databases::
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
|
@ -244,20 +244,20 @@ databases::
|
||||||
'USER': 'mysql_user',
|
'USER': 'mysql_user',
|
||||||
'PASSWORD': 'swordfish',
|
'PASSWORD': 'swordfish',
|
||||||
},
|
},
|
||||||
'master': {
|
'leader': {
|
||||||
'NAME': 'master',
|
'NAME': 'leader',
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'USER': 'mysql_user',
|
'USER': 'mysql_user',
|
||||||
'PASSWORD': 'spam',
|
'PASSWORD': 'spam',
|
||||||
},
|
},
|
||||||
'slave1': {
|
'follower1': {
|
||||||
'NAME': 'slave1',
|
'NAME': 'follower1',
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'USER': 'mysql_user',
|
'USER': 'mysql_user',
|
||||||
'PASSWORD': 'eggs',
|
'PASSWORD': 'eggs',
|
||||||
},
|
},
|
||||||
'slave2': {
|
'follower2': {
|
||||||
'NAME': 'slave2',
|
'NAME': 'follower2',
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'USER': 'mysql_user',
|
'USER': 'mysql_user',
|
||||||
'PASSWORD': 'bacon',
|
'PASSWORD': 'bacon',
|
||||||
|
@ -309,30 +309,30 @@ send queries for the ``auth`` app to ``auth_db``::
|
||||||
return None
|
return None
|
||||||
|
|
||||||
And we also want a router that sends all other apps to the
|
And we also want a router that sends all other apps to the
|
||||||
master/slave configuration, and randomly chooses a slave to read
|
leader/follower configuration, and randomly chooses a follower to read
|
||||||
from::
|
from::
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
class MasterSlaveRouter(object):
|
class LeaderFollowerRouter(object):
|
||||||
def db_for_read(self, model, **hints):
|
def db_for_read(self, model, **hints):
|
||||||
"""
|
"""
|
||||||
Reads go to a randomly-chosen slave.
|
Reads go to a randomly-chosen follower.
|
||||||
"""
|
"""
|
||||||
return random.choice(['slave1', 'slave2'])
|
return random.choice(['follower1', 'follower2'])
|
||||||
|
|
||||||
def db_for_write(self, model, **hints):
|
def db_for_write(self, model, **hints):
|
||||||
"""
|
"""
|
||||||
Writes always go to master.
|
Writes always go to leader.
|
||||||
"""
|
"""
|
||||||
return 'master'
|
return 'leader'
|
||||||
|
|
||||||
def allow_relation(self, obj1, obj2, **hints):
|
def allow_relation(self, obj1, obj2, **hints):
|
||||||
"""
|
"""
|
||||||
Relations between objects are allowed if both objects are
|
Relations between objects are allowed if both objects are
|
||||||
in the master/slave pool.
|
in the leader/follower pool.
|
||||||
"""
|
"""
|
||||||
db_list = ('master', 'slave1', 'slave2')
|
db_list = ('leader', 'follower1', 'follower2')
|
||||||
if obj1._state.db in db_list and obj2._state.db in db_list:
|
if obj1._state.db in db_list and obj2._state.db in db_list:
|
||||||
return True
|
return True
|
||||||
return None
|
return None
|
||||||
|
@ -347,17 +347,17 @@ Finally, in the settings file, we add the following (substituting
|
||||||
``path.to.`` with the actual python path to the module(s) where the
|
``path.to.`` with the actual python path to the module(s) where the
|
||||||
routers are defined)::
|
routers are defined)::
|
||||||
|
|
||||||
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.MasterSlaveRouter']
|
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.LeaderFollowerRouter']
|
||||||
|
|
||||||
The order in which routers are processed is significant. Routers will
|
The order in which routers are processed is significant. Routers will
|
||||||
be queried in the order the are listed in the
|
be queried in the order the are listed in the
|
||||||
:setting:`DATABASE_ROUTERS` setting . In this example, the
|
:setting:`DATABASE_ROUTERS` setting . In this example, the
|
||||||
``AuthRouter`` is processed before the ``MasterSlaveRouter``, and as a
|
``AuthRouter`` is processed before the ``LeaderFollowerRouter``, and as a
|
||||||
result, decisions concerning the models in ``auth`` are processed
|
result, decisions concerning the models in ``auth`` are processed
|
||||||
before any other decision is made. If the :setting:`DATABASE_ROUTERS`
|
before any other decision is made. If the :setting:`DATABASE_ROUTERS`
|
||||||
setting listed the two routers in the other order,
|
setting listed the two routers in the other order,
|
||||||
``MasterSlaveRouter.allow_migrate()`` would be processed first. The
|
``LeaderFollowerRouter.allow_migrate()`` would be processed first. The
|
||||||
catch-all nature of the MasterSlaveRouter implementation would mean
|
catch-all nature of the LeaderFollowerRouter implementation would mean
|
||||||
that all models would be available on all databases.
|
that all models would be available on all databases.
|
||||||
|
|
||||||
With this setup installed, lets run some Django code::
|
With this setup installed, lets run some Django code::
|
||||||
|
@ -369,7 +369,7 @@ With this setup installed, lets run some Django code::
|
||||||
>>> # This save will also be directed to 'auth_db'
|
>>> # This save will also be directed to 'auth_db'
|
||||||
>>> fred.save()
|
>>> fred.save()
|
||||||
|
|
||||||
>>> # These retrieval will be randomly allocated to a slave database
|
>>> # These retrieval will be randomly allocated to a follower database
|
||||||
>>> dna = Person.objects.get(name='Douglas Adams')
|
>>> dna = Person.objects.get(name='Douglas Adams')
|
||||||
|
|
||||||
>>> # A new object has no database allocation when created
|
>>> # A new object has no database allocation when created
|
||||||
|
@ -379,10 +379,10 @@ With this setup installed, lets run some Django code::
|
||||||
>>> # the same database as the author object
|
>>> # the same database as the author object
|
||||||
>>> mh.author = dna
|
>>> mh.author = dna
|
||||||
|
|
||||||
>>> # This save will force the 'mh' instance onto the master database...
|
>>> # This save will force the 'mh' instance onto the leader database...
|
||||||
>>> mh.save()
|
>>> mh.save()
|
||||||
|
|
||||||
>>> # ... but if we re-retrieve the object, it will come back on a slave
|
>>> # ... but if we re-retrieve the object, it will come back on a follower
|
||||||
>>> mh = Book.objects.get(title='Mostly Harmless')
|
>>> mh = Book.objects.get(title='Mostly Harmless')
|
||||||
|
|
||||||
|
|
||||||
|
@ -690,7 +690,7 @@ In addition, some objects are automatically created just after
|
||||||
database).
|
database).
|
||||||
|
|
||||||
For common setups with multiple databases, it isn't useful to have these
|
For common setups with multiple databases, it isn't useful to have these
|
||||||
objects in more than one database. Common setups include master / slave and
|
objects in more than one database. Common setups include leader / follower and
|
||||||
connecting to external databases. Therefore, it's recommended:
|
connecting to external databases. Therefore, it's recommended:
|
||||||
|
|
||||||
- either to run :djadmin:`migrate` only for the default database;
|
- either to run :djadmin:`migrate` only for the default database;
|
||||||
|
|
|
@ -64,16 +64,16 @@ The following is a simple unit test using the request factory::
|
||||||
Tests and multiple databases
|
Tests and multiple databases
|
||||||
============================
|
============================
|
||||||
|
|
||||||
.. _topics-testing-masterslave:
|
.. _topics-testing-leaderfollower:
|
||||||
|
|
||||||
Testing master/slave configurations
|
Testing leader/follower configurations
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
If you're testing a multiple database configuration with master/slave
|
If you're testing a multiple database configuration with leader/follower
|
||||||
replication, this strategy of creating test databases poses a problem.
|
replication, this strategy of creating test databases poses a problem.
|
||||||
When the test databases are created, there won't be any replication,
|
When the test databases are created, there won't be any replication,
|
||||||
and as a result, data created on the master won't be seen on the
|
and as a result, data created on the leader won't be seen on the
|
||||||
slave.
|
follower.
|
||||||
|
|
||||||
To compensate for this, Django allows you to define that a database is
|
To compensate for this, Django allows you to define that a database is
|
||||||
a *test mirror*. Consider the following (simplified) example database
|
a *test mirror*. Consider the following (simplified) example database
|
||||||
|
@ -83,34 +83,34 @@ configuration::
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'NAME': 'myproject',
|
'NAME': 'myproject',
|
||||||
'HOST': 'dbmaster',
|
'HOST': 'dbleader',
|
||||||
# ... plus some other settings
|
# ... plus some other settings
|
||||||
},
|
},
|
||||||
'slave': {
|
'follower': {
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'NAME': 'myproject',
|
'NAME': 'myproject',
|
||||||
'HOST': 'dbslave',
|
'HOST': 'dbfollower',
|
||||||
'TEST_MIRROR': 'default'
|
'TEST_MIRROR': 'default'
|
||||||
# ... plus some other settings
|
# ... plus some other settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
In this setup, we have two database servers: ``dbmaster``, described
|
In this setup, we have two database servers: ``dbleader``, described
|
||||||
by the database alias ``default``, and ``dbslave`` described by the
|
by the database alias ``default``, and ``dbfollower`` described by the
|
||||||
alias ``slave``. As you might expect, ``dbslave`` has been configured
|
alias ``follower``. As you might expect, ``dbfollower`` has been configured
|
||||||
by the database administrator as a read slave of ``dbmaster``, so in
|
by the database administrator as a read follower of ``dbleader``, so in
|
||||||
normal activity, any write to ``default`` will appear on ``slave``.
|
normal activity, any write to ``default`` will appear on ``follower``.
|
||||||
|
|
||||||
If Django created two independent test databases, this would break any
|
If Django created two independent test databases, this would break any
|
||||||
tests that expected replication to occur. However, the ``slave``
|
tests that expected replication to occur. However, the ``follower``
|
||||||
database has been configured as a test mirror (using the
|
database has been configured as a test mirror (using the
|
||||||
:setting:`TEST_MIRROR` setting), indicating that under testing,
|
:setting:`TEST_MIRROR` setting), indicating that under testing,
|
||||||
``slave`` should be treated as a mirror of ``default``.
|
``follower`` should be treated as a mirror of ``default``.
|
||||||
|
|
||||||
When the test environment is configured, a test version of ``slave``
|
When the test environment is configured, a test version of ``follower``
|
||||||
will *not* be created. Instead the connection to ``slave``
|
will *not* be created. Instead the connection to ``follower``
|
||||||
will be redirected to point at ``default``. As a result, writes to
|
will be redirected to point at ``default``. As a result, writes to
|
||||||
``default`` will appear on ``slave`` -- but because they are actually
|
``default`` will appear on ``follower`` -- but because they are actually
|
||||||
the same database, not because there is data replication between the
|
the same database, not because there is data replication between the
|
||||||
two databases.
|
two databases.
|
||||||
|
|
||||||
|
|
|
@ -978,7 +978,7 @@ class MultiDBOperationTests(MigrationTestBase):
|
||||||
multi_db = True
|
multi_db = True
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Make the 'other' database appear to be a slave of the 'default'
|
# Make the 'other' database appear to be a follower of the 'default'
|
||||||
self.old_routers = router.routers
|
self.old_routers = router.routers
|
||||||
router.routers = [MigrateNothingRouter()]
|
router.routers = [MigrateNothingRouter()]
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.db import DEFAULT_DB_ALIAS
|
||||||
|
|
||||||
|
|
||||||
class TestRouter(object):
|
class TestRouter(object):
|
||||||
# A test router. The behavior is vaguely master/slave, but the
|
# A test router. The behavior is vaguely leader/follower, but the
|
||||||
# databases aren't assumed to propagate changes.
|
# databases aren't assumed to propagate changes.
|
||||||
def db_for_read(self, model, instance=None, **hints):
|
def db_for_read(self, model, instance=None, **hints):
|
||||||
if instance:
|
if instance:
|
||||||
|
|
|
@ -854,7 +854,7 @@ class QueryTestCase(TestCase):
|
||||||
self.assertEqual(book.editor._state.db, 'other')
|
self.assertEqual(book.editor._state.db, 'other')
|
||||||
|
|
||||||
def test_subquery(self):
|
def test_subquery(self):
|
||||||
"""Make sure as_sql works with subqueries and master/slave."""
|
"""Make sure as_sql works with subqueries and leader/follower."""
|
||||||
sub = Person.objects.using('other').filter(name='fff')
|
sub = Person.objects.using('other').filter(name='fff')
|
||||||
qs = Book.objects.filter(editor__in=sub)
|
qs = Book.objects.filter(editor__in=sub)
|
||||||
|
|
||||||
|
@ -919,7 +919,7 @@ class RouterTestCase(TestCase):
|
||||||
multi_db = True
|
multi_db = True
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Make the 'other' database appear to be a slave of the 'default'
|
# Make the 'other' database appear to be a follower of the 'default'
|
||||||
self.old_routers = router.routers
|
self.old_routers = router.routers
|
||||||
router.routers = [TestRouter()]
|
router.routers = [TestRouter()]
|
||||||
|
|
||||||
|
@ -1071,7 +1071,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
dive.editor = marty
|
dive.editor = marty
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments of original objects haven't changed...
|
# Database assignments of original objects haven't changed...
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1089,7 +1089,7 @@ class RouterTestCase(TestCase):
|
||||||
except Book.DoesNotExist:
|
except Book.DoesNotExist:
|
||||||
self.fail('Source database should have a copy of saved object')
|
self.fail('Source database should have a copy of saved object')
|
||||||
|
|
||||||
# This isn't a real master-slave database, so restore the original from other
|
# This isn't a real leader-follower database, so restore the original from other
|
||||||
dive = Book.objects.using('other').get(title='Dive into Python')
|
dive = Book.objects.using('other').get(title='Dive into Python')
|
||||||
self.assertEqual(dive._state.db, 'other')
|
self.assertEqual(dive._state.db, 'other')
|
||||||
|
|
||||||
|
@ -1097,7 +1097,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
marty.edited = [pro, dive]
|
marty.edited = [pro, dive]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Assignment implies a save, so database assignments of original objects have changed...
|
# Assignment implies a save, so database assignments of original objects have changed...
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1111,7 +1111,7 @@ class RouterTestCase(TestCase):
|
||||||
except Book.DoesNotExist:
|
except Book.DoesNotExist:
|
||||||
self.fail('Source database should have a copy of saved object')
|
self.fail('Source database should have a copy of saved object')
|
||||||
|
|
||||||
# This isn't a real master-slave database, so restore the original from other
|
# This isn't a real leader-follower database, so restore the original from other
|
||||||
dive = Book.objects.using('other').get(title='Dive into Python')
|
dive = Book.objects.using('other').get(title='Dive into Python')
|
||||||
self.assertEqual(dive._state.db, 'other')
|
self.assertEqual(dive._state.db, 'other')
|
||||||
|
|
||||||
|
@ -1119,7 +1119,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
marty.edited.add(dive)
|
marty.edited.add(dive)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Add implies a save, so database assignments of original objects have changed...
|
# Add implies a save, so database assignments of original objects have changed...
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1133,7 +1133,7 @@ class RouterTestCase(TestCase):
|
||||||
except Book.DoesNotExist:
|
except Book.DoesNotExist:
|
||||||
self.fail('Source database should have a copy of saved object')
|
self.fail('Source database should have a copy of saved object')
|
||||||
|
|
||||||
# This isn't a real master-slave database, so restore the original from other
|
# This isn't a real leader-follower database, so restore the original from other
|
||||||
dive = Book.objects.using('other').get(title='Dive into Python')
|
dive = Book.objects.using('other').get(title='Dive into Python')
|
||||||
|
|
||||||
# If you assign a FK object when the base object hasn't
|
# If you assign a FK object when the base object hasn't
|
||||||
|
@ -1196,7 +1196,7 @@ class RouterTestCase(TestCase):
|
||||||
mark = Person.objects.using('default').create(pk=2, name="Mark Pilgrim")
|
mark = Person.objects.using('default').create(pk=2, name="Mark Pilgrim")
|
||||||
|
|
||||||
# Now save back onto the usual database.
|
# Now save back onto the usual database.
|
||||||
# This simulates master/slave - the objects exist on both database,
|
# This simulates leader/follower - the objects exist on both database,
|
||||||
# but the _state.db is as it is for all other tests.
|
# but the _state.db is as it is for all other tests.
|
||||||
pro.save(using='default')
|
pro.save(using='default')
|
||||||
marty.save(using='default')
|
marty.save(using='default')
|
||||||
|
@ -1213,7 +1213,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
marty.book_set = [pro, dive]
|
marty.book_set = [pro, dive]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments don't change
|
# Database assignments don't change
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1232,7 +1232,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
marty.book_set.add(dive)
|
marty.book_set.add(dive)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments don't change
|
# Database assignments don't change
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1251,7 +1251,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
dive.authors = [mark, marty]
|
dive.authors = [mark, marty]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments don't change
|
# Database assignments don't change
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1273,7 +1273,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
dive.authors.add(marty)
|
dive.authors.add(marty)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments don't change
|
# Database assignments don't change
|
||||||
self.assertEqual(marty._state.db, 'default')
|
self.assertEqual(marty._state.db, 'default')
|
||||||
|
@ -1311,7 +1311,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
bob.userprofile = alice_profile
|
bob.userprofile = alice_profile
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments of original objects haven't changed...
|
# Database assignments of original objects haven't changed...
|
||||||
self.assertEqual(alice._state.db, 'default')
|
self.assertEqual(alice._state.db, 'default')
|
||||||
|
@ -1342,7 +1342,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
review1.content_object = dive
|
review1.content_object = dive
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments of original objects haven't changed...
|
# Database assignments of original objects haven't changed...
|
||||||
self.assertEqual(pro._state.db, 'default')
|
self.assertEqual(pro._state.db, 'default')
|
||||||
|
@ -1361,7 +1361,7 @@ class RouterTestCase(TestCase):
|
||||||
except Book.DoesNotExist:
|
except Book.DoesNotExist:
|
||||||
self.fail('Source database should have a copy of saved object')
|
self.fail('Source database should have a copy of saved object')
|
||||||
|
|
||||||
# This isn't a real master-slave database, so restore the original from other
|
# This isn't a real leader-follower database, so restore the original from other
|
||||||
dive = Book.objects.using('other').get(title='Dive into Python')
|
dive = Book.objects.using('other').get(title='Dive into Python')
|
||||||
self.assertEqual(dive._state.db, 'other')
|
self.assertEqual(dive._state.db, 'other')
|
||||||
|
|
||||||
|
@ -1369,7 +1369,7 @@ class RouterTestCase(TestCase):
|
||||||
try:
|
try:
|
||||||
dive.reviews.add(review1)
|
dive.reviews.add(review1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across master/slave databases with a common source should be ok")
|
self.fail("Assignment across leader/follower databases with a common source should be ok")
|
||||||
|
|
||||||
# Database assignments of original objects haven't changed...
|
# Database assignments of original objects haven't changed...
|
||||||
self.assertEqual(pro._state.db, 'default')
|
self.assertEqual(pro._state.db, 'default')
|
||||||
|
@ -1444,7 +1444,7 @@ class RouterTestCase(TestCase):
|
||||||
self.assertEqual(pro.reviews.db_manager('default').all().db, 'default')
|
self.assertEqual(pro.reviews.db_manager('default').all().db, 'default')
|
||||||
|
|
||||||
def test_subquery(self):
|
def test_subquery(self):
|
||||||
"""Make sure as_sql works with subqueries and master/slave."""
|
"""Make sure as_sql works with subqueries and leader/follower."""
|
||||||
# Create a book and author on the other database
|
# Create a book and author on the other database
|
||||||
|
|
||||||
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
||||||
|
@ -1482,7 +1482,7 @@ class AuthTestCase(TestCase):
|
||||||
multi_db = True
|
multi_db = True
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Make the 'other' database appear to be a slave of the 'default'
|
# Make the 'other' database appear to be a follower of the 'default'
|
||||||
self.old_routers = router.routers
|
self.old_routers = router.routers
|
||||||
router.routers = [AuthRouter()]
|
router.routers = [AuthRouter()]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue