From c8873bbba76bc52ab0766f98e8537b2fa66be71c Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 27 Jan 2010 07:56:53 +0000 Subject: [PATCH] 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 --- django/db/utils.py | 30 +++++++++++----- docs/topics/db/multi-db.txt | 6 +++- .../multiple_database/tests.py | 36 +++++++++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/django/db/utils.py b/django/db/utils.py index 1b3218e34c..da34570a76 100644 --- a/django/db/utils.py +++ b/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 diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index f1d67bf977..73872e6b53 100644 --- a/docs/topics/db/multi-db.txt +++ b/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 diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index dc668eab5b..fd6e64476d 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/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")