From a75dc3406f63ec621f2bc3f9af79152c0df92182 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 31 Mar 2010 12:33:10 +0000 Subject: [PATCH] Fixed #13250 -- Corrected a problem with the use of routing rules on the create() call on a Foreign Key. Thanks to chris@xlevus.net for the report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12895 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/contenttypes/generic.py | 5 +-- django/db/models/fields/related.py | 3 +- .../multiple_database/tests.py | 33 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index dfc857d5f1..102779257a 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -5,7 +5,7 @@ Classes allowing "generic" relations through ContentType and object-id fields. from django.core.exceptions import ObjectDoesNotExist from django.db import connection from django.db.models import signals -from django.db import models +from django.db import models, router from django.db.models.fields.related import RelatedField, Field, ManyToManyRel from django.db.models.loading import get_model from django.forms import ModelForm @@ -271,7 +271,8 @@ def create_generic_related_manager(superclass): def create(self, **kwargs): kwargs[self.content_type_field_name] = self.content_type kwargs[self.object_id_field_name] = self.pk_val - return super(GenericRelatedObjectManager, self).create(**kwargs) + db = router.db_for_write(self.model, instance=self.instance) + return super(GenericRelatedObjectManager, self).using(db).create(**kwargs) create.alters_data = True return GenericRelatedObjectManager diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 2e63a49bcf..0e1addf1a4 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -419,7 +419,8 @@ class ForeignRelatedObjectsDescriptor(object): def create(self, **kwargs): kwargs.update({rel_field.name: instance}) - return super(RelatedManager, self).create(**kwargs) + db = router.db_for_write(rel_model, instance=instance) + return super(RelatedManager, self).using(db).create(**kwargs) create.alters_data = True def get_or_create(self, **kwargs): diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 540648f0e8..28aa7c249d 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -971,6 +971,19 @@ class RouterTestCase(TestCase): water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark) self.assertEquals(water._state.db, 'default') + # If you create an object through a FK relation, it will be + # written to the write database, even if the original object + # was on the read database + cheesecake = mark.edited.create(title='Dive into Cheesecake', published=datetime.date(2010, 3, 15)) + self.assertEquals(cheesecake._state.db, 'default') + + # Same goes for get_or_create, regardless of whether getting or creating + cheesecake, created = mark.edited.get_or_create(title='Dive into Cheesecake', published=datetime.date(2010, 3, 15)) + self.assertEquals(cheesecake._state.db, 'default') + + puddles, created = mark.edited.get_or_create(title='Dive into Puddles', published=datetime.date(2010, 3, 15)) + self.assertEquals(puddles._state.db, 'default') + def test_m2m_cross_database_protection(self): "M2M relations can cross databases if the database share a source" # Create books and authors on the inverse to the usual database @@ -1074,6 +1087,19 @@ class RouterTestCase(TestCase): self.assertEquals(Book.authors.through.objects.using('default').count(), 1) self.assertEquals(Book.authors.through.objects.using('other').count(), 0) + # If you create an object through a M2M relation, it will be + # written to the write database, even if the original object + # was on the read database + alice = dive.authors.create(name='Alice') + self.assertEquals(alice._state.db, 'default') + + # Same goes for get_or_create, regardless of whether getting or creating + alice, created = dive.authors.get_or_create(name='Alice') + self.assertEquals(alice._state.db, 'default') + + bob, created = dive.authors.get_or_create(name='Bob') + self.assertEquals(bob._state.db, 'default') + def test_generic_key_cross_database_protection(self): "Generic Key operations can span databases if they share a source" # Create a book and author on the default database @@ -1150,6 +1176,13 @@ class RouterTestCase(TestCase): review3.content_object = dive self.assertEquals(review3._state.db, 'default') + # If you create an object through a M2M relation, it will be + # written to the write database, even if the original object + # was on the read database + dive = Book.objects.using('other').get(title='Dive into Python') + nyt = dive.reviews.create(source="New York Times", content_object=dive) + self.assertEquals(nyt._state.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