Browse Source

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.
James Bennett 12 years ago
parent
commit
5d1f09f450
1 changed files with 116 additions and 60 deletions
  1. 116 60
      docs/topics/db/multi-db.txt

+ 116 - 60
docs/topics/db/multi-db.txt

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