diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 66e3a110aa..081f750d93 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -48,8 +48,8 @@ class SiteProfileNotAvailable(Exception): pass class PermissionManager(models.Manager): - def get_by_natural_key(self, codename, app_label, model): - return self.get( + def get_by_natural_key(self, codename, app_label, model, using=DEFAULT_DB_ALIAS): + return self.using(using).get( codename=codename, content_type=ContentType.objects.get_by_natural_key(app_label, model) ) diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 6ca9f08c73..e7dd14fbe7 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -159,7 +159,7 @@ class Command(BaseCommand): print "Installing %s fixture '%s' from %s." % \ (format, fixture_name, humanize(fixture_dir)) try: - objects = serializers.deserialize(format, fixture) + objects = serializers.deserialize(format, fixture, using=using) for obj in objects: if obj.object._meta.app_label not in excluded_apps: objects_in_fixture += 1 diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py index 1a2eb4f6cc..32f135009c 100644 --- a/django/core/serializers/__init__.py +++ b/django/core/serializers/__init__.py @@ -36,14 +36,14 @@ except ImportError: _serializers = {} def register_serializer(format, serializer_module, serializers=None): - """"Register a new serializer. - + """"Register a new serializer. + ``serializer_module`` should be the fully qualified module name for the serializer. - + If ``serializers`` is provided, the registration will be added to the provided dictionary. - + If ``serializers`` is not provided, the registration will be made directly into the global register of serializers. Adding serializers directly is not a thread-safe operation. @@ -53,7 +53,7 @@ def register_serializer(format, serializer_module, serializers=None): _serializers[format] = module else: serializers[format] = module - + def unregister_serializer(format): "Unregister a given serializer. This is not a thread-safe operation." del _serializers[format] @@ -87,7 +87,7 @@ def serialize(format, queryset, **options): s.serialize(queryset, **options) return s.getvalue() -def deserialize(format, stream_or_string): +def deserialize(format, stream_or_string, **options): """ Deserialize a stream or a string. Returns an iterator that yields ``(obj, m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* -- @@ -95,7 +95,7 @@ def deserialize(format, stream_or_string): list_of_related_objects}``. """ d = get_deserializer(format) - return d(stream_or_string) + return d(stream_or_string, **options) def _load_serializers(): """ diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index d5872fefc3..27c7c30e5b 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -39,7 +39,7 @@ def Deserializer(stream_or_string, **options): stream = StringIO(stream_or_string) else: stream = stream_or_string - for obj in PythonDeserializer(simplejson.load(stream)): + for obj in PythonDeserializer(simplejson.load(stream), **options): yield obj class DjangoJSONEncoder(simplejson.JSONEncoder): diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index c6c2457258..b29ae15c3c 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -6,7 +6,7 @@ other serializers. from django.conf import settings from django.core.serializers import base -from django.db import models +from django.db import models, DEFAULT_DB_ALIAS from django.utils.encoding import smart_unicode, is_protected_type class Serializer(base.Serializer): @@ -77,6 +77,7 @@ def Deserializer(object_list, **options): It's expected that you pass the Python objects themselves (instead of a stream or a string) to the constructor """ + db = options.pop('using', DEFAULT_DB_ALIAS) models.get_apps() for d in object_list: # Look up the model and starting build a dict of data for it. @@ -96,7 +97,7 @@ def Deserializer(object_list, **options): if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): def m2m_convert(value): if hasattr(value, '__iter__'): - return field.rel.to._default_manager.get_by_natural_key(*value).pk + return field.rel.to._default_manager.get_by_natural_key(*value, **{'using':db}).pk else: return smart_unicode(field.rel.to._meta.pk.to_python(value)) else: @@ -108,7 +109,7 @@ def Deserializer(object_list, **options): if field_value is not None: if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): if hasattr(field_value, '__iter__'): - obj = field.rel.to._default_manager.get_by_natural_key(*field_value) + obj = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':db}) value = getattr(obj, field.rel.field_name) else: value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py index 7a302e615e..e136c88952 100644 --- a/django/core/serializers/pyyaml.py +++ b/django/core/serializers/pyyaml.py @@ -58,6 +58,6 @@ def Deserializer(stream_or_string, **options): stream = StringIO(stream_or_string) else: stream = stream_or_string - for obj in PythonDeserializer(yaml.load(stream)): + for obj in PythonDeserializer(yaml.load(stream), **options): yield obj diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index 7e4f9a2eb8..0b808801f7 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -4,7 +4,7 @@ XML serializer. from django.conf import settings from django.core.serializers import base -from django.db import models +from django.db import models, DEFAULT_DB_ALIAS from django.utils.xmlutils import SimplerXMLGenerator from django.utils.encoding import smart_unicode from xml.dom import pulldom @@ -149,6 +149,7 @@ class Deserializer(base.Deserializer): def __init__(self, stream_or_string, **options): super(Deserializer, self).__init__(stream_or_string, **options) self.event_stream = pulldom.parse(self.stream) + self.db = options.pop('using', DEFAULT_DB_ALIAS) def next(self): for event, node in self.event_stream: @@ -218,7 +219,7 @@ class Deserializer(base.Deserializer): if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] - obj = field.rel.to._default_manager.get_by_natural_key(*field_value) + obj = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':self.db}) obj_pk = getattr(obj, field.rel.field_name) else: # Otherwise, treat like a normal PK @@ -239,7 +240,7 @@ class Deserializer(base.Deserializer): if keys: # If there are 'natural' subelements, it must be a natural key field_value = [getInnerText(k).strip() for k in keys] - obj_pk = field.rel.to._default_manager.get_by_natural_key(*field_value).pk + obj_pk = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':self.db}).pk else: # Otherwise, treat like a normal PK value. obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk')) diff --git a/django/db/models/base.py b/django/db/models/base.py index eb484ea3bc..73c2c3903c 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -614,7 +614,7 @@ class Model(object): param = smart_str(getattr(self, field.attname)) q = Q(**{'%s__%s' % (field.name, op): param}) q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk}) - qs = self.__class__._default_manager.filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order) + qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order) try: return qs[0] except IndexError: diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py index 7fcf89d561..4be9a9c17c 100644 --- a/tests/modeltests/fixtures/models.py +++ b/tests/modeltests/fixtures/models.py @@ -57,8 +57,8 @@ class Tag(models.Model): self.tagged, self.name) class PersonManager(models.Manager): - def get_by_natural_key(self, name): - return self.get(name=name) + def get_by_natural_key(self, name, using=DEFAULT_DB_ALIAS): + return self.using(using).get(name=name) class Person(models.Model): objects = PersonManager() diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index 0c3e8c9f7b..dab8d3b8fa 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -83,8 +83,8 @@ class WidgetProxy(Widget): # Check for forward references in FKs and M2Ms with natural keys class TestManager(models.Manager): - def get_by_natural_key(self, key): - return self.get(name=key) + def get_by_natural_key(self, key, using=DEFAULT_DB_ALIAS): + return self.using(using).get(name=key) class Store(models.Model): objects = TestManager() diff --git a/tests/regressiontests/multiple_database/fixtures/multidb.default.json b/tests/regressiontests/multiple_database/fixtures/multidb.default.json index ecfcea4c2a..379b18a803 100644 --- a/tests/regressiontests/multiple_database/fixtures/multidb.default.json +++ b/tests/regressiontests/multiple_database/fixtures/multidb.default.json @@ -1,10 +1,26 @@ [ + { + "pk": 1, + "model": "multiple_database.person", + "fields": { + "name": "Marty Alchin" + } + }, + { + "pk": 2, + "model": "multiple_database.person", + "fields": { + "name": "George Vilches" + } + }, { "pk": 2, "model": "multiple_database.book", "fields": { "title": "Pro Django", - "published": "2008-12-16" + "published": "2008-12-16", + "authors": [["Marty Alchin"]], + "editor": ["George Vilches"] } } ] diff --git a/tests/regressiontests/multiple_database/fixtures/multidb.other.json b/tests/regressiontests/multiple_database/fixtures/multidb.other.json index eaa600ae73..c64f490201 100644 --- a/tests/regressiontests/multiple_database/fixtures/multidb.other.json +++ b/tests/regressiontests/multiple_database/fixtures/multidb.other.json @@ -1,10 +1,26 @@ [ + { + "pk": 1, + "model": "multiple_database.person", + "fields": { + "name": "Mark Pilgrim" + } + }, + { + "pk": 2, + "model": "multiple_database.person", + "fields": { + "name": "Chris Mills" + } + }, { "pk": 2, "model": "multiple_database.book", "fields": { "title": "Dive into Python", - "published": "2009-5-4" + "published": "2009-5-4", + "authors": [["Mark Pilgrim"]], + "editor": ["Chris Mills"] } } ] \ No newline at end of file diff --git a/tests/regressiontests/multiple_database/models.py b/tests/regressiontests/multiple_database/models.py index bd664c94e3..88ab88e9eb 100644 --- a/tests/regressiontests/multiple_database/models.py +++ b/tests/regressiontests/multiple_database/models.py @@ -16,10 +16,25 @@ class Review(models.Model): class Meta: ordering = ('source',) +class PersonManager(models.Manager): + def get_by_natural_key(self, name, using=DEFAULT_DB_ALIAS): + return self.using(using).get(name=name) + +class Person(models.Model): + objects = PersonManager() + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + + class Meta: + ordering = ('name',) + class Book(models.Model): title = models.CharField(max_length=100) published = models.DateField() - authors = models.ManyToManyField('Author') + authors = models.ManyToManyField(Person) + editor = models.ForeignKey(Person, null=True, related_name='edited') reviews = generic.GenericRelation(Review) def __unicode__(self): @@ -28,16 +43,6 @@ class Book(models.Model): class Meta: ordering = ('title',) -class Author(models.Model): - name = models.CharField(max_length=100) - favourite_book = models.ForeignKey(Book, null=True, related_name='favourite_of') - - def __unicode__(self): - return self.name - - class Meta: - ordering = ('name',) - class UserProfile(models.Model): user = models.OneToOneField(User) flavor = models.CharField(max_length=100) diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 1633674d07..4030d74540 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import User from django.db import connections from django.test import TestCase -from models import Book, Author, Review, UserProfile +from models import Book, Person, Review, UserProfile try: # we only have these models if the user is using multi-db, it's safe the @@ -133,13 +133,13 @@ class QueryTestCase(TestCase): pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) - marty = Author.objects.create(name="Marty Alchin") + marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations pro.authors = [marty] @@ -163,7 +163,7 @@ class QueryTestCase(TestCase): # Reget the objects to clear caches dive = Book.objects.using('other').get(title="Dive into Python") - mark = Author.objects.using('other').get(name="Mark Pilgrim") + mark = Person.objects.using('other').get(name="Mark Pilgrim") # Retrive related object by descriptor. Related objects should be database-baound self.assertEquals(list(dive.authors.all().values_list('name', flat=True)), @@ -178,13 +178,13 @@ class QueryTestCase(TestCase): dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations dive.authors = [mark] # Add a second author - john = Author.objects.using('other').create(name="John Smith") + john = Person.objects.using('other').create(name="John Smith") self.assertEquals(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), []) @@ -222,7 +222,7 @@ class QueryTestCase(TestCase): dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations dive.authors = [mark] @@ -233,30 +233,30 @@ class QueryTestCase(TestCase): # Add a books to the m2m mark.book_set.add(grease) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), [u'Mark Pilgrim']) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), [u'Mark Pilgrim']) # Remove a book from the m2m mark.book_set.remove(grease) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), [u'Mark Pilgrim']) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), []) # Clear the books associated with mark mark.book_set.clear() - self.assertEquals(list(Author.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), []) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), []) # Create a book through the m2m interface mark.book_set.create(title="Dive into HTML5", published=datetime.date(2020, 1, 1)) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), []) - self.assertEquals(list(Author.objects.using('other').filter(book__title='Dive into HTML5').values_list('name', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into HTML5').values_list('name', flat=True)), [u'Mark Pilgrim']) def test_m2m_cross_database_protection(self): @@ -265,13 +265,13 @@ class QueryTestCase(TestCase): pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) - marty = Author.objects.create(name="Marty Alchin") + marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") # Set a foreign key set with an object from a different database try: marty.book_set = [pro, dive] @@ -313,91 +313,93 @@ class QueryTestCase(TestCase): pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) - marty = Author.objects.create(name="Marty Alchin") + marty = Person.objects.create(name="Marty Alchin") + george = Person.objects.create(name="George Vilches") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") + chris = Person.objects.using('other').create(name="Chris Mills") # Save the author's favourite books - marty.favourite_book = pro - marty.save() + pro.editor = george + pro.save() - mark.favourite_book = dive - mark.save() + dive.editor = chris + dive.save() - marty = Author.objects.using('default').get(name="Marty Alchin") - self.assertEquals(marty.favourite_book.title, "Pro Django") + pro = Book.objects.using('default').get(title="Pro Django") + self.assertEquals(pro.editor.name, "George Vilches") - mark = Author.objects.using('other').get(name='Mark Pilgrim') - self.assertEquals(mark.favourite_book.title, "Dive into Python") + dive = Book.objects.using('other').get(title="Dive into Python") + self.assertEquals(dive.editor.name, "Chris Mills") # Check that queries work across foreign key joins - self.assertEquals(list(Book.objects.using('default').filter(favourite_of__name='Marty Alchin').values_list('title', flat=True)), - [u'Pro Django']) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Marty Alchin').values_list('title', flat=True)), + self.assertEquals(list(Person.objects.using('default').filter(edited__title='Pro Django').values_list('name', flat=True)), + [u'George Vilches']) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Pro Django').values_list('name', flat=True)), []) - self.assertEquals(list(Book.objects.using('default').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), + self.assertEquals(list(Person.objects.using('default').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), - [u'Dive into Python']) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), + [u'Chris Mills']) # Reget the objects to clear caches + chris = Person.objects.using('other').get(name="Chris Mills") dive = Book.objects.using('other').get(title="Dive into Python") - mark = Author.objects.using('other').get(name="Mark Pilgrim") # Retrive related object by descriptor. Related objects should be database-baound - self.assertEquals(list(dive.favourite_of.all().values_list('name', flat=True)), - [u'Mark Pilgrim']) - - self.assertEquals(mark.favourite_book.title, u'Dive into Python') + self.assertEquals(list(chris.edited.values_list('title', flat=True)), + [u'Dive into Python']) def test_foreign_key_reverse_operations(self): "FK reverse manipulations are all constrained to a single DB" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") + chris = Person.objects.using('other').create(name="Chris Mills") # Save the author relations - mark.favourite_book = dive - mark.save() + dive.editor = chris + dive.save() - # Add a second author - john = Author.objects.using('other').create(name="John Smith") - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='John Smith').values_list('title', flat=True)), + # Add a second book edited by chris + html5 = Book.objects.using('other').create(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) + chris.edited.add(html5) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), + [u'Chris Mills']) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), + [u'Chris Mills']) - dive.favourite_of.add(john) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), - [u'Dive into Python']) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='John Smith').values_list('title', flat=True)), - [u'Dive into Python']) - - # Remove the second author - dive.favourite_of.remove(john) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), - [u'Dive into Python']) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='John Smith').values_list('title', flat=True)), + # Remove the second editor + chris.edited.remove(html5) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), + [u'Chris Mills']) - # Clear all favourite_of - dive.favourite_of.clear() - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), + # Clear all edited books + chris.edited.clear() + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='John Smith').values_list('title', flat=True)), + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) # Create an author through the m2m interface - dive.favourite_of.create(name='Jane Brown') - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), + chris.edited.create(title='Dive into Water', published=datetime.date(2010, 3, 15)) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), + []) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Water').values_list('name', flat=True)), + [u'Chris Mills']) + self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) - self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Jane Brown').values_list('title', flat=True)), - [u'Dive into Python']) def test_foreign_key_cross_database_protection(self): "Operations that involve sharing FK objects across databases raise an error" @@ -405,31 +407,31 @@ class QueryTestCase(TestCase): pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) - marty = Author.objects.create(name="Marty Alchin") + marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) - mark = Author.objects.using('other').create(name="Mark Pilgrim") + mark = Person.objects.using('other').create(name="Mark Pilgrim") # Set a foreign key with an object from a different database try: - marty.favourite_book = dive + dive.editor = marty self.fail("Shouldn't be able to assign across databases") except ValueError: pass # Set a foreign key set with an object from a different database try: - dive.favourite_of = [mark, marty] + marty.edited = [pro, dive] self.fail("Shouldn't be able to assign across databases") except ValueError: pass # Add to a foreign key set with an object from a different database try: - dive.favourite_of.add(marty) + marty.edited.add(dive) self.fail("Shouldn't be able to assign across databases") except ValueError: pass @@ -437,37 +439,50 @@ class QueryTestCase(TestCase): # BUT! if you assign a FK object when the base object hasn't # been saved yet, you implicitly assign the database for the # base object. - john = Author(name="John Smith") + chris = Person(name="Chris Mills") + html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) # initially, no db assigned - self.assertEquals(john._state.db, None) + self.assertEquals(chris._state.db, None) + self.assertEquals(html5._state.db, None) - # Dive comes from 'other', so john is set to use 'other'... - john.favourite_book = dive - self.assertEquals(john._state.db, 'other') + # old object comes from 'other', so the new object is set to use 'other'... + dive.editor = chris + html5.editor = mark + # self.assertEquals(chris._state.db, 'other') + self.assertEquals(html5._state.db, 'other') # ... but it isn't saved yet - self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), + self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)), [u'Mark Pilgrim']) + self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), + [u'Dive into Python']) - # When saved, John goes to 'other' - john.save() - self.assertEquals(list(Author.objects.using('default').values_list('name',flat=True)), + # When saved (no using required), new objects goes to 'other' + chris.save() + html5.save() + self.assertEquals(list(Person.objects.using('default').values_list('name',flat=True)), [u'Marty Alchin']) - self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), - [u'John Smith', u'Mark Pilgrim']) + self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)), + [u'Chris Mills', u'Mark Pilgrim']) + self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), + [u'Pro Django']) + self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), + [u'Dive into HTML5', u'Dive into Python']) # This also works if you assign the FK in the constructor - jane = Author(name='Jane Brown', favourite_book=dive) - self.assertEquals(jane._state.db, 'other') + water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark) + self.assertEquals(water._state.db, 'other') # ... but it isn't saved yet - self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), - [u'John Smith', u'Mark Pilgrim']) + self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), + [u'Pro Django']) + self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), + [u'Dive into HTML5', u'Dive into Python']) - # When saved, Jane goes to 'other' - jane.save() - self.assertEquals(list(Author.objects.using('default').values_list('name',flat=True)), - [u'Marty Alchin']) - self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), - [u'Jane Brown', u'John Smith', u'Mark Pilgrim']) + # When saved, the new book goes to 'other' + water.save() + self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), + [u'Pro Django']) + self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), + [u'Dive into HTML5', u'Dive into Python', u'Dive into Water']) def test_generic_key_separation(self): "Generic fields are constrained to a single database" @@ -591,6 +606,21 @@ class QueryTestCase(TestCase): self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)), [u'Python Daily', u'Python Weekly']) + def test_ordering(self): + "get_next_by_XXX commands stick to a single database" + pro = Book.objects.create(title="Pro Django", + published=datetime.date(2008, 12, 16)) + + dive = Book.objects.using('other').create(title="Dive into Python", + published=datetime.date(2009, 5, 4)) + + learn = Book.objects.using('other').create(title="Learning Python", + published=datetime.date(2008, 7, 16)) + + self.assertEquals(learn.get_next_by_published().title, "Dive into Python") + self.assertEquals(dive.get_previous_by_published().title, "Learning Python") + + class UserProfileTestCase(TestCase): def setUp(self): self.old_auth_profile_module = getattr(settings, 'AUTH_PROFILE_MODULE', None) @@ -624,7 +654,7 @@ class FixtureTestCase(TestCase): Book.objects.get(title="Pro Django") Book.objects.using('default').get(title="Pro Django") except Book.DoesNotExist: - self.fail('"Dive Into Python" should exist on default database') + self.fail('"Pro Django" should exist on default database') self.assertRaises(Book.DoesNotExist, Book.objects.using('other').get,