Ticket 18657: Fix inconsistent DB names in router example.

This rewrites the entire example to use the same DB names throughout,
and also is hopefully a bit more sensibly described. Additionally, the
missing import of the random module for choosing a read slave is
included in the example now.
This commit is contained in:
James Bennett 2012-09-08 16:02:00 -04:00
parent d7853c55ed
commit 5d1f09f450
1 changed files with 108 additions and 52 deletions

View File

@ -201,73 +201,129 @@ An example
write to propagate to the slaves). It also doesn't consider the write to propagate to the slaves). 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? Say you want ``myapp`` to So - what does this mean in practice? Let's consider another sample
exist on the ``other`` database, and you want all other models in a configuration. This one will have several databases: one for the
master/slave relationship between the databases ``master``, ``slave1`` and ``auth`` application, and all other apps using a master/slave setup
``slave2``. To implement this, you would need 2 routers:: with two read slaves. Here are the settings specifying these
databases::
class MyAppRouter(object): DATABASES = {
"""A router to control all database operations on models in 'auth_db': {
the myapp application""" 'NAME': 'auth_db',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'swordfish',
},
'master': {
'NAME': 'master',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'spam',
},
'slave1': {
'NAME': 'slave1',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'eggs',
},
'slave2': {
'NAME': 'slave2',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'bacon',
},
}
def db_for_read(self, model, **hints): Now we'll need to handle routing. First we want a router that knows to
"Point all operations on myapp models to 'other'" send queries for the ``auth`` app to ``auth_db``::
if model._meta.app_label == 'myapp':
return 'other'
return None
def db_for_write(self, model, **hints): class AuthRouter(object):
"Point all operations on myapp models to 'other'" """
if model._meta.app_label == 'myapp': A router to control all database operations on models in the
return 'other' auth application.
return None """
def db_for_read(self, model, **hints):
"""
Attempts to read auth models go to auth_db.
"""
if model._meta.app_label == 'auth':
return 'auth_db'
return None
def allow_relation(self, obj1, obj2, **hints): def db_for_write(self, model, **hints):
"Allow any relation if a model in myapp is involved" """
if obj1._meta.app_label == 'myapp' or obj2._meta.app_label == 'myapp': Attempts to write auth models go to auth_db.
return True """
return None if model._meta.app_label == 'auth':
return 'auth_db'
return Non
def allow_syncdb(self, db, model): def allow_relation(self, obj1, obj2, **hints):
"Make sure the myapp app only appears on the 'other' db" """
if db == 'other': Allow relations if a model in the auth app is involved.
return model._meta.app_label == 'myapp' """
elif model._meta.app_label == 'myapp': if obj1._meta.app_label == 'auth' or \
return False obj2._meta.app_label == 'auth':
return None return True
return None
def allow_syncdb(self, db, model):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
if db == 'auth_db':
return model._meta.app_label == 'auth'
elif model._meta.app_label == 'auth':
return False
return None
And we also want a router that sends all other apps to the
master/slave configuration, and randomly chooses a slave to read
from::
import random
class MasterSlaveRouter(object): class MasterSlaveRouter(object):
"""A router that sets up a simple master/slave configuration"""
def db_for_read(self, model, **hints): def db_for_read(self, model, **hints):
"Point all read operations to a random slave" """
return random.choice(['slave1','slave2']) Reads go to a randomly-chosen slave.
"""
return random.choice(['slave1', 'slave2'])
def db_for_write(self, model, **hints): def db_for_write(self, model, **hints):
"Point all write operations to the master" """
return 'master' Writes always go to master.
"""
return 'master'
def allow_relation(self, obj1, obj2, **hints): def allow_relation(self, obj1, obj2, **hints):
"Allow any relation between two objects in the db pool" """
db_list = ('master','slave1','slave2') Relations between objects are allowed if both objects are
if obj1._state.db in db_list and obj2._state.db in db_list: in the master/slave pool.
return True """
return None db_list = ('master', 'slave1', 'slave2')
if obj1.state.db in db_list and obj2.state.db in db_list:
return True
return None
def allow_syncdb(self, db, model): def allow_syncdb(self, db, model):
"Explicitly put all models on all databases." """
return True All non-auth models end up in this pool.
"""
return True
Then, in your settings file, add the following (substituting ``path.to.`` with Finally, in the settings file, we add the following (substituting
the actual python path to the module where you define the routers):: ``path.to.`` with the actual python path to the module(s) where the
routers are defined)::
DATABASE_ROUTERS = ['path.to.MyAppRouter', 'path.to.MasterSlaveRouter'] DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.MasterSlaveRouter']
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
``MyAppRouter`` is processed before the ``MasterSlaveRouter``, and as a ``AuthRouter`` is processed before the ``MasterSlaveRouter``, and as a
result, decisions concerning the models in ``myapp`` 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_syncdb()`` would be processed first. The ``MasterSlaveRouter.allow_syncdb()`` would be processed first. The
@ -276,11 +332,11 @@ 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::
>>> # This retrieval will be performed on the 'credentials' database >>> # This retrieval will be performed on the 'auth_db' database
>>> fred = User.objects.get(username='fred') >>> fred = User.objects.get(username='fred')
>>> fred.first_name = 'Frederick' >>> fred.first_name = 'Frederick'
>>> # This save will also be directed to 'credentials' >>> # 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 slave database