Skip to content

Commit

Permalink
Fixed #13358 -- Ensured that db_manager() can be used to override dat…
Browse files Browse the repository at this point in the history
…abase 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
  • Loading branch information
freakboy3742 committed Apr 16, 2010
1 parent 94a968c commit 2cd48ba
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 5 deletions.
9 changes: 6 additions & 3 deletions django/contrib/contenttypes/generic.py
Expand Up @@ -243,11 +243,12 @@ def __init__(self, model=None, core_filters=None, instance=None, symmetrical=Non
self.pk_val = self.instance._get_pk_val() self.pk_val = self.instance._get_pk_val()


def get_query_set(self): def get_query_set(self):
db = self._db or router.db_for_read(self.model, instance=self.instance)
query = { query = {
'%s__pk' % self.content_type_field_name : self.content_type.id, '%s__pk' % self.content_type_field_name : self.content_type.id,
'%s__exact' % self.object_id_field_name : self.pk_val, '%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): def add(self, *objs):
for obj in objs: for obj in objs:
Expand All @@ -259,13 +260,15 @@ def add(self, *objs):
add.alters_data = True add.alters_data = True


def remove(self, *objs): def remove(self, *objs):
db = router.db_for_write(self.model, instance=self.instance)
for obj in objs: for obj in objs:
obj.delete(using=self.instance._state.db) obj.delete(using=db)
remove.alters_data = True remove.alters_data = True


def clear(self): def clear(self):
db = router.db_for_write(self.model, instance=self.instance)
for obj in self.all(): for obj in self.all():
obj.delete(using=self.instance._state.db) obj.delete(using=db)
clear.alters_data = True clear.alters_data = True


def create(self, **kwargs): def create(self, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions django/db/models/fields/related.py
Expand Up @@ -406,7 +406,7 @@ def create_manager(self, instance, superclass):


class RelatedManager(superclass): class RelatedManager(superclass):
def get_query_set(self): 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)) return superclass.get_query_set(self).using(db).filter(**(self.core_filters))


def add(self, *objs): def add(self, *objs):
Expand Down Expand Up @@ -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__) 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): 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)) return superclass.get_query_set(self).using(db)._next_is_sticky().filter(**(self.core_filters))


# If the ManyToMany relation has an intermediary model, # If the ManyToMany relation has an intermediary model,
Expand Down
39 changes: 39 additions & 0 deletions tests/regressiontests/multiple_database/tests.py
Expand Up @@ -1186,6 +1186,45 @@ def test_generic_key_cross_database_protection(self):
nyt = dive.reviews.create(source="New York Times", content_object=dive) nyt = dive.reviews.create(source="New York Times", content_object=dive)
self.assertEquals(nyt._state.db, 'default') 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): def test_subquery(self):
"""Make sure as_sql works with subqueries and master/slave.""" """Make sure as_sql works with subqueries and master/slave."""
# Create a book and author on the other database # Create a book and author on the other database
Expand Down

0 comments on commit 2cd48ba

Please sign in to comment.