瀏覽代碼

Made the database master router tolerant of router definitions that omit individual routing methods.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12304 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Russell Keith-Magee 15 年之前
父節點
當前提交
c8873bbba7
共有 3 個文件被更改,包括 62 次插入10 次删除
  1. 21 9
      django/db/utils.py
  2. 5 1
      docs/topics/db/multi-db.txt
  3. 36 0
      tests/regressiontests/multiple_database/tests.py

+ 21 - 9
django/db/utils.py

@@ -103,9 +103,13 @@ class ConnectionRouter(object):
         def _route_db(self, model, **hints):
             chosen_db = None
             for router in self.routers:
-                chosen_db = getattr(router, action)(model, **hints)
-                if chosen_db:
-                    return chosen_db
+                try:
+                    chosen_db = getattr(router, action)(model, **hints)
+                    if chosen_db:
+                        return chosen_db
+                except AttributeError:
+                    # If the router doesn't have a method, skip to the next one.
+                    pass
             try:
                 return hints['instance']._state.db or DEFAULT_DB_ALIAS
             except KeyError:
@@ -117,14 +121,22 @@ class ConnectionRouter(object):
 
     def allow_relation(self, obj1, obj2, **hints):
         for router in self.routers:
-            allow = router.allow_relation(obj1, obj2, **hints)
-            if allow is not None:
-                return allow
+            try:
+                allow = router.allow_relation(obj1, obj2, **hints)
+                if allow is not None:
+                    return allow
+            except AttributeError:
+                # If the router doesn't have a method, skip to the next one.
+                pass
         return obj1._state.db == obj2._state.db
 
     def allow_syncdb(self, db, model):
         for router in self.routers:
-            allow = router.allow_syncdb(db, model)
-            if allow is not None:
-                return allow
+            try:
+                allow = router.allow_syncdb(db, model)
+                if allow is not None:
+                    return allow
+            except AttributeError:
+                # If the router doesn't have a method, skip to the next one.
+                pass
         return True

+ 5 - 1
docs/topics/db/multi-db.txt

@@ -99,7 +99,7 @@ routing scheme.
 Database routers
 ----------------
 
-A database Router is a class that provides four methods:
+A database Router is a class that provides up to four methods:
 
 .. method:: db_for_read(model, **hints)
 
@@ -141,6 +141,10 @@ A database Router is a class that provides four methods:
     the router has no opinion. This method can be used to determine
     the availability of a model on a given database.
 
+A router doesn't have to provide *all* these methods - it omit one or
+more of them. If one of the methods is omitted, Django will skip that
+router when performing the relevant check.
+
 .. _topics-db-multi-db-hints:
 
 Hints

+ 36 - 0
tests/regressiontests/multiple_database/tests.py

@@ -674,6 +674,11 @@ class AuthRouter(object):
             return False
         return None
 
+class WriteRouter(object):
+    # A router that only expresses an opinion on writes
+    def db_for_write(self, model, **hints):
+        return 'writer'
+
 class RouterTestCase(TestCase):
     multi_db = True
 
@@ -724,6 +729,37 @@ class RouterTestCase(TestCase):
         self.assertTrue(router.allow_syncdb('other', User))
         self.assertFalse(router.allow_syncdb('other', Book))
 
+    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",
+                                                  published=datetime.date(2009, 5, 4))
+
+        # First check the baseline behaviour
+
+        self.assertEquals(router.db_for_read(User), 'other')
+        self.assertEquals(router.db_for_read(Book), 'other')
+
+        self.assertEquals(router.db_for_write(User), 'default')
+        self.assertEquals(router.db_for_write(Book), 'default')
+
+        self.assertTrue(router.allow_relation(dive, dive))
+
+        self.assertTrue(router.allow_syncdb('default', User))
+        self.assertTrue(router.allow_syncdb('default', Book))
+
+        router.routers = [WriteRouter(), AuthRouter(), TestRouter()]
+
+        self.assertEquals(router.db_for_read(User), 'other')
+        self.assertEquals(router.db_for_read(Book), 'other')
+
+        self.assertEquals(router.db_for_write(User), 'writer')
+        self.assertEquals(router.db_for_write(Book), 'writer')
+
+        self.assertTrue(router.allow_relation(dive, dive))
+
+        self.assertFalse(router.allow_syncdb('default', User))
+        self.assertTrue(router.allow_syncdb('default', Book))
+
 
     def test_database_routing(self):
         marty = Person.objects.using('default').create(name="Marty Alchin")