From 2cd48bac7ee7a5c4dded039439f9b6aaf566696e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 16 Apr 2010 12:29:23 +0000 Subject: [PATCH] Fixed #13358 -- Ensured that db_manager() can be used to override database routing on M2M, reverse FK and generic key queries. Thanks to Craig Kimerer for the report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12993 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/contenttypes/generic.py | 9 +++-- django/db/models/fields/related.py | 4 +- .../multiple_database/tests.py | 39 +++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 102779257a43a..40c33361d7e68 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -243,11 +243,12 @@ def __init__(self, model=None, core_filters=None, instance=None, symmetrical=Non self.pk_val = self.instance._get_pk_val() def get_query_set(self): + db = self._db or router.db_for_read(self.model, instance=self.instance) query = { '%s__pk' % self.content_type_field_name : self.content_type.id, '%s__exact' % self.object_id_field_name : self.pk_val, } - return superclass.get_query_set(self).using(self.instance._state.db).filter(**query) + return superclass.get_query_set(self).using(db).filter(**query) def add(self, *objs): for obj in objs: @@ -259,13 +260,15 @@ def add(self, *objs): add.alters_data = True def remove(self, *objs): + db = router.db_for_write(self.model, instance=self.instance) for obj in objs: - obj.delete(using=self.instance._state.db) + obj.delete(using=db) remove.alters_data = True def clear(self): + db = router.db_for_write(self.model, instance=self.instance) for obj in self.all(): - obj.delete(using=self.instance._state.db) + obj.delete(using=db) clear.alters_data = True def create(self, **kwargs): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 0677f2cda029f..1e016fa7e1979 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -406,7 +406,7 @@ def create_manager(self, instance, superclass): class RelatedManager(superclass): def get_query_set(self): - db = router.db_for_read(rel_model, instance=instance) + db = self._db or router.db_for_read(rel_model, instance=instance) return superclass.get_query_set(self).using(db).filter(**(self.core_filters)) def add(self, *objs): @@ -480,7 +480,7 @@ def __init__(self, model=None, core_filters=None, instance=None, symmetrical=Non raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % instance.__class__.__name__) def get_query_set(self): - db = router.db_for_read(self.instance.__class__, instance=self.instance) + db = self._db or router.db_for_read(self.instance.__class__, instance=self.instance) return superclass.get_query_set(self).using(db)._next_is_sticky().filter(**(self.core_filters)) # If the ManyToMany relation has an intermediary model, diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 0aac69401a1c2..e4929f474210b 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -1186,6 +1186,45 @@ def test_generic_key_cross_database_protection(self): nyt = dive.reviews.create(source="New York Times", content_object=dive) self.assertEquals(nyt._state.db, 'default') + def test_m2m_managers(self): + "M2M relations are represented by managers, and can be controlled like managers" + pro = Book.objects.using('other').create(pk=1, title="Pro Django", + published=datetime.date(2008, 12, 16)) + + marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") + pro.authors = [marty] + + self.assertEquals(pro.authors.db, 'other') + self.assertEquals(pro.authors.db_manager('default').db, 'default') + self.assertEquals(pro.authors.db_manager('default').all().db, 'default') + + self.assertEquals(marty.book_set.db, 'other') + self.assertEquals(marty.book_set.db_manager('default').db, 'default') + self.assertEquals(marty.book_set.db_manager('default').all().db, 'default') + + def test_foreign_key_managers(self): + "FK reverse relations are represented by managers, and can be controlled like managers" + marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") + pro = Book.objects.using('other').create(pk=1, title="Pro Django", + published=datetime.date(2008, 12, 16), + editor=marty) + + self.assertEquals(marty.edited.db, 'other') + self.assertEquals(marty.edited.db_manager('default').db, 'default') + self.assertEquals(marty.edited.db_manager('default').all().db, 'default') + + def test_generic_key_managers(self): + "Generic key relations are represented by managers, and can be controlled like managers" + pro = Book.objects.using('other').create(title="Pro Django", + published=datetime.date(2008, 12, 16)) + + review1 = Review.objects.using('other').create(source="Python Monthly", + content_object=pro) + + self.assertEquals(pro.reviews.db, 'other') + self.assertEquals(pro.reviews.db_manager('default').db, 'default') + self.assertEquals(pro.reviews.db_manager('default').all().db, 'default') + def test_subquery(self): """Make sure as_sql works with subqueries and master/slave.""" # Create a book and author on the other database