Author: Alex
Date: 2009-11-23 10:42:56 -0600 (Mon, 23 Nov 2009)
New Revision: 11764

Added:
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb-common.json
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.default.json
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.other.json
Modified:
   django/branches/soc2009/multidb/TODO
   django/branches/soc2009/multidb/django/db/backends/__init__.py
   django/branches/soc2009/multidb/django/db/backends/creation.py
   django/branches/soc2009/multidb/django/db/utils.py
   django/branches/soc2009/multidb/django/test/simple.py
   django/branches/soc2009/multidb/django/test/testcases.py
   django/branches/soc2009/multidb/docs/internals/contributing.txt
   django/branches/soc2009/multidb/docs/ref/settings.txt
   django/branches/soc2009/multidb/docs/topics/testing.txt
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/models.py
   
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/tests.py
Log:
[soc2009/multidb] Updated testing services to handle multiple databases better. 
 Includes extra tests (some failing) for multiple database support.  Patch from 
Russell Keith-Magee.

Modified: django/branches/soc2009/multidb/TODO
===================================================================
--- django/branches/soc2009/multidb/TODO        2009-11-23 16:42:38 UTC (rev 
11763)
+++ django/branches/soc2009/multidb/TODO        2009-11-23 16:42:56 UTC (rev 
11764)
@@ -13,10 +13,6 @@
  * Resolve the public facing UI issues around using multi-db
    * Should we take the opportunity to modify DB backends to use fully 
qualified paths?
    * Meta.using? Is is still required/desirable?
- * Testing infrastructure
-    * Most tests don't need multidb. Some absolutely require it, but only to 
prove you
-      can write to a different db. Second DB could be a SQLite temp file. Need 
to have
-      test infrastructure to allow creation of the temp database.
  * Cleanup of new API entry points
     * validate() on a field
         * name/purpose clash with Honza?

Modified: django/branches/soc2009/multidb/django/db/backends/__init__.py
===================================================================
--- django/branches/soc2009/multidb/django/db/backends/__init__.py      
2009-11-23 16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/django/db/backends/__init__.py      
2009-11-23 16:42:56 UTC (rev 11764)
@@ -26,7 +26,7 @@
     """
     ops = None
 
-    def __init__(self, settings_dict):
+    def __init__(self, settings_dict, alias='default'):
         # `settings_dict` should be a dictionary containing keys such as
         # DATABASE_NAME, DATABASE_USER, etc. It's called `settings_dict`
         # instead of `settings` to disambiguate it from Django settings
@@ -34,6 +34,7 @@
         self.connection = None
         self.queries = []
         self.settings_dict = settings_dict
+        self.alias = alias
 
     def __eq__(self, other):
         return self.settings_dict == other.settings_dict
@@ -117,7 +118,7 @@
     row.
     """
     compiler_module = "django.db.models.sql.compiler"
-    
+
     def __init__(self):
         self._cache = {}
 

Modified: django/branches/soc2009/multidb/django/db/backends/creation.py
===================================================================
--- django/branches/soc2009/multidb/django/db/backends/creation.py      
2009-11-23 16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/django/db/backends/creation.py      
2009-11-23 16:42:56 UTC (rev 11764)
@@ -316,13 +316,13 @@
                 output.append(ds)
         return output
 
-    def create_test_db(self, verbosity=1, autoclobber=False, alias=None):
+    def create_test_db(self, verbosity=1, autoclobber=False):
         """
         Creates a test database, prompting the user for confirmation if the
         database already exists. Returns the name of the test database created.
         """
         if verbosity >= 1:
-            print "Creating test database..."
+            print "Creating test database '%s'..." % self.connection.alias
 
         test_database_name = self._create_test_db(verbosity, autoclobber)
 
@@ -334,7 +334,7 @@
         # FIXME we end up loading the same fixture into the default DB for each
         # DB we have, this causes various test failures, but can't really be
         # fixed until we have an API for saving to a specific DB
-        call_command('syncdb', verbosity=verbosity, interactive=False, 
database=alias)
+        call_command('syncdb', verbosity=verbosity, interactive=False, 
database=self.connection.alias)
 
         if settings.CACHE_BACKEND.startswith('db://'):
             from django.core.cache import parse_backend_uri
@@ -404,7 +404,7 @@
         database already exists. Returns the name of the test database created.
         """
         if verbosity >= 1:
-            print "Destroying test database..."
+            print "Destroying test database '%s'..." % self.connection.alias
         self.connection.close()
         test_database_name = self.connection.settings_dict['DATABASE_NAME']
         self.connection.settings_dict['DATABASE_NAME'] = old_database_name

Modified: django/branches/soc2009/multidb/django/db/utils.py
===================================================================
--- django/branches/soc2009/multidb/django/db/utils.py  2009-11-23 16:42:38 UTC 
(rev 11763)
+++ django/branches/soc2009/multidb/django/db/utils.py  2009-11-23 16:42:56 UTC 
(rev 11764)
@@ -67,7 +67,7 @@
         self.ensure_defaults(alias)
         db = self.databases[alias]
         backend = load_backend(db['DATABASE_ENGINE'])
-        conn = backend.DatabaseWrapper(db)
+        conn = backend.DatabaseWrapper(db, alias)
         self._connections[alias] = conn
         return conn
 
@@ -76,19 +76,3 @@
 
     def all(self):
         return [self[alias] for alias in self]
-
-    def alias_for_connection(self, connection):
-        """
-        Returns the alias for the given connection object.
-        """
-        return self.alias_for_settings(connection.settings_dict)
-
-    def alias_for_settings(self, settings_dict):
-        """
-        Returns the alias for the given settings dictionary.
-        """
-        for alias in self:
-            conn_settings = self.databases[alias]
-            if conn_settings == settings_dict:
-                return alias
-        return None

Modified: django/branches/soc2009/multidb/django/test/simple.py
===================================================================
--- django/branches/soc2009/multidb/django/test/simple.py       2009-11-23 
16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/django/test/simple.py       2009-11-23 
16:42:56 UTC (rev 11764)
@@ -191,7 +191,7 @@
     for alias in connections:
         connection = connections[alias]
         old_names.append((connection, 
connection.settings_dict['DATABASE_NAME']))
-        connection.creation.create_test_db(verbosity, autoclobber=not 
interactive, alias=alias)
+        connection.creation.create_test_db(verbosity, autoclobber=not 
interactive)
     result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
     for connection, old_name in old_names:
         connection.creation.destroy_test_db(old_name, verbosity)

Modified: django/branches/soc2009/multidb/django/test/testcases.py
===================================================================
--- django/branches/soc2009/multidb/django/test/testcases.py    2009-11-23 
16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/django/test/testcases.py    2009-11-23 
16:42:56 UTC (rev 11764)
@@ -7,7 +7,7 @@
 from django.core import mail
 from django.core.management import call_command
 from django.core.urlresolvers import clear_url_caches
-from django.db import transaction, connections
+from django.db import transaction, connections, DEFAULT_DB_ALIAS
 from django.http import QueryDict
 from django.test import _doctest as doctest
 from django.test.client import Client
@@ -225,12 +225,20 @@
         mail.outbox = []
 
     def _fixture_setup(self):
-        call_command('flush', verbosity=0, interactive=False)
-        if hasattr(self, 'fixtures'):
-            # We have to use this slightly awkward syntax due to the fact
-            # that we're using *args and **kwargs together.
-            call_command('loaddata', *self.fixtures, **{'verbosity': 0})
+        # If the test case has a multi_db=True flag, flush all databases.
+        # Otherwise, just flush default.
+        if getattr(self, 'multi_db', False):
+            databases = connections
+        else:
+            databases = [DEFAULT_DB_ALIAS]
+        for db in databases:
+            call_command('flush', verbosity=0, interactive=False, database=db)
 
+            if hasattr(self, 'fixtures'):
+                # We have to use this slightly awkward syntax due to the fact
+                # that we're using *args and **kwargs together.
+                call_command('loaddata', *self.fixtures, **{'verbosity': 0, 
'database': db})
+
     def _urlconf_setup(self):
         if hasattr(self, 'urls'):
             self._old_root_urlconf = settings.ROOT_URLCONF
@@ -453,27 +461,44 @@
         if not connections_support_transactions():
             return super(TestCase, self)._fixture_setup()
 
-        for conn in connections:
-            transaction.enter_transaction_management(using=conn)
-            transaction.managed(True, using=conn)
+        # If the test case has a multi_db=True flag, setup all databases.
+        # Otherwise, just use default.
+        if getattr(self, 'multi_db', False):
+            databases = connections
+        else:
+            databases = [DEFAULT_DB_ALIAS]
+
+        for db in databases:
+            transaction.enter_transaction_management(using=db)
+            transaction.managed(True, using=db)
         disable_transaction_methods()
 
         from django.contrib.sites.models import Site
         Site.objects.clear_cache()
 
-        if hasattr(self, 'fixtures'):
-            call_command('loaddata', *self.fixtures, **{
-                                                        'verbosity': 0,
-                                                        'commit': False
-                                                        })
+        for db in databases:
+            if hasattr(self, 'fixtures'):
+                call_command('loaddata', *self.fixtures, **{
+                                                            'verbosity': 0,
+                                                            'commit': False,
+                                                            'database': db
+                                                            })
 
     def _fixture_teardown(self):
         if not connections_support_transactions():
             return super(TestCase, self)._fixture_teardown()
 
+        # If the test case has a multi_db=True flag, teardown all databases.
+        # Otherwise, just teardown default.
+        if getattr(self, 'multi_db', False):
+            databases = connections
+        else:
+            databases = [DEFAULT_DB_ALIAS]
+
         restore_transaction_methods()
-        for conn in connections:
-            transaction.rollback(using=conn)
-            transaction.leave_transaction_management(using=conn)
+        for db in databases:
+            transaction.rollback(using=db)
+            transaction.leave_transaction_management(using=db)
+
         for connection in connections.all():
             connection.close()

Modified: django/branches/soc2009/multidb/docs/internals/contributing.txt
===================================================================
--- django/branches/soc2009/multidb/docs/internals/contributing.txt     
2009-11-23 16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/docs/internals/contributing.txt     
2009-11-23 16:42:56 UTC (rev 11764)
@@ -752,20 +752,46 @@
     ./runtests.py --settings=path.to.django.settings
 
 Yes, the unit tests need a settings module, but only for database connection
-info, with the ``DATABASES`` setting.
+info. Your :setting:`DATABASES` setting needs to define two databases:
 
-If you're using the ``sqlite3`` database backend, no further settings are
-needed. A temporary database will be created in memory when running the tests.
+    * A ``default`` database. This database should use the backend that
+      you want to use for primary testing
 
-If you're using another backend:
+    * A database with the alias ``other``. The ``other`` database is
+      used to establish that queries can be directed to different
+      databases. As a result, this database can use any backend you
+      want. It doesn't need to use the same backend as the ``default``
+      database (although it can use the same backend if you want to).
 
-    * Your the ``DATABASE_USER`` option for each of your databases needs to
+If you're using the ``sqlite3`` database backend, you need to define
+:setting:`DATABASE_ENGINE` for both databases, plus a
+:setting:`TEST_DATABASE_NAME` for the ``other`` database. The
+following is a minimal settings file that can be used to test SQLite::
+
+    DATABASES = {
+        'default': {
+            'DATABASE_ENGINE': 'sqlite3'
+        },
+        'other': {
+            'DATABASE_ENGINE': 'sqlite3',
+            'TEST_DATABASE_NAME: 'other_db'
+        }
+    }
+
+
+If you're using another backend, you will need to provide other details for
+each database:
+
+    * The :setting:`DATABASE_USER` option for each of your databases needs to
       specify an existing user account for the database.
 
-    * The ``DATABASE_NAME`` option must be the name of an existing database to
+    * The :setting:`DATABASE_PASSWORD` option needs to provide the password for
+      the :setting:`DATABASE_USER` that has been specified.
+
+    * The :setting:`DATABASE_NAME` option must be the name of an existing 
database to
       which the given user has permission to connect. The unit tests will not
       touch this database; the test runner creates a new database whose name is
-      ``DATABASE_NAME`` prefixed with ``test_``, and this test database is
+      :setting:`DATABASE_NAME` prefixed with ``test_``, and this test database 
is
       deleted when the tests are finished. This means your user account needs
       permission to execute ``CREATE DATABASE``.
 

Modified: django/branches/soc2009/multidb/docs/ref/settings.txt
===================================================================
--- django/branches/soc2009/multidb/docs/ref/settings.txt       2009-11-23 
16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/docs/ref/settings.txt       2009-11-23 
16:42:56 UTC (rev 11764)
@@ -202,11 +202,11 @@
 containing the options for an individual database.  The following inner options
 are used:
 
-.. admonition:: Note
+.. deprecated: 1.2
 
-    In versions of Django prior to 1.2 each of the following were individual
-    settings, the usage of those has been deprecated but will be supported
-    until Django 1.4.
+    In versions of Django prior to 1.2 each of the following were
+    individual settings. The usage of the standalone database settings
+    has been deprecated, and will be removed in Django 1.4.
 
 .. setting:: DATABASE_ENGINE
 

Modified: django/branches/soc2009/multidb/docs/topics/testing.txt
===================================================================
--- django/branches/soc2009/multidb/docs/topics/testing.txt     2009-11-23 
16:42:38 UTC (rev 11763)
+++ django/branches/soc2009/multidb/docs/topics/testing.txt     2009-11-23 
16:42:56 UTC (rev 11764)
@@ -1037,6 +1037,39 @@
 
 .. _emptying-test-outbox:
 
+Multi-database support
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. attribute:: TestCase.multi_db
+
+.. versionadded:: 1.2
+
+Django sets up a test database corresponding to every database that is
+defined in the :setting:``DATABASES`` definition in your settings
+file. However, a big part of the time taken to run a Django TestCase
+is consumed by the call to ``flush`` that ensures that you have a
+clean database at the start of each test run. If you have multiple
+databases, multiple flushes are required (one for each database),
+which can be a time consuming activity -- especially if your tests
+don't need to test multi-database activity.
+
+As an optimization, Django only flushes the ``default`` database at
+the start of each test run. If your setup contains multiple databases,
+and you have a test that requires every database to be clean, you can
+use the ``multi_db`` attribute on the test suite to request a full
+flush.
+
+For example::
+
+    class TestMyViews(TestCase):
+        multi_db = True
+
+        def testIndexPageView(self):
+            call_some_test_code()
+
+This test case will flush *all* the test databases before running
+``testIndexPageView``.
+
 Emptying the test outbox
 ~~~~~~~~~~~~~~~~~~~~~~~~
 

Added: 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb-common.json
===================================================================
--- 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb-common.json
                                (rev 0)
+++ 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb-common.json
        2009-11-23 16:42:56 UTC (rev 11764)
@@ -0,0 +1,10 @@
+[
+    {
+        "pk": 1,
+        "model": "multiple_database.book",
+        "fields": {
+            "title": "The Definitive Guide to Django",
+            "published": "2009-7-8"
+        }
+    }
+]
\ No newline at end of file

Added: 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.default.json
===================================================================
--- 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.default.json
                               (rev 0)
+++ 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.default.json
       2009-11-23 16:42:56 UTC (rev 11764)
@@ -0,0 +1,10 @@
+[
+    {
+        "pk": 2,
+        "model": "multiple_database.book",
+        "fields": {
+            "title": "Dive into Python",
+            "published": "2009-5-4"
+        }
+    }
+]
\ No newline at end of file

Added: 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.other.json
===================================================================
--- 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.other.json
                         (rev 0)
+++ 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/fixtures/multidb.other.json
 2009-11-23 16:42:56 UTC (rev 11764)
@@ -0,0 +1,10 @@
+[
+    {
+        "pk": 2,
+        "model": "multiple_database.book",
+        "fields": {
+            "title": "Pro Django",
+            "published": "2008-12-16"
+        }
+    }
+]
\ No newline at end of file

Modified: 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/models.py
===================================================================
--- 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/models.py
   2009-11-23 16:42:38 UTC (rev 11763)
+++ 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/models.py
   2009-11-23 16:42:56 UTC (rev 11764)
@@ -14,19 +14,7 @@
 
 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
-
-if len(settings.DATABASES) > 1:
-    article_using = filter(lambda o: o != DEFAULT_DB_ALIAS, 
settings.DATABASES.keys())[0]
-    class Article(models.Model):
-        title = models.CharField(max_length=100)
-        author = models.ForeignKey(Author)
-
-        def __unicode__(self):
-            return self.title
-
-        class Meta:
-            ordering = ('title',)
-            using = article_using

Modified: 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/tests.py
===================================================================
--- 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/tests.py
    2009-11-23 16:42:38 UTC (rev 11763)
+++ 
django/branches/soc2009/multidb/tests/regressiontests/multiple_database/tests.py
    2009-11-23 16:42:56 UTC (rev 11764)
@@ -14,82 +14,243 @@
 except ImportError:
     pass
 
-class ConnectionHandlerTestCase(TestCase):
-    def test_alias_for_connection(self):
-        for db in connections:
-            self.assertEqual(db, 
connections.alias_for_connection(connections[db]))
+class QueryTestCase(TestCase):
+    multi_db = True
 
+    def test_default_creation(self):
+        "Objects created on the default database don't leak onto other 
databases"
+        # Create a book on the default database using create()
+        Book.objects.create(title="Dive into Python",
+            published=datetime.date(2009, 5, 4))
 
-class QueryTestCase(TestCase):
+        # Create a book on the default database using a save
+        pro = Book()
+        pro.title="Pro Django"
+        pro.published = datetime.date(2008, 12, 16)
+        pro.save()
+
+        # Check that book exists on the default database, but not on other 
database
+        try:
+            Book.objects.get(title="Dive into Python")
+            Book.objects.using('default').get(title="Dive into Python")
+        except Book.DoesNotExist:
+            self.fail('"Dive Into Python" should exist on default database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('other').get,
+            title="Dive into Python"
+        )
+
+        try:
+            Book.objects.get(title="Pro Django")
+            Book.objects.using('default').get(title="Pro Django")
+        except Book.DoesNotExist:
+            self.fail('"Pro Django" should exist on default database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('other').get,
+            title="Pro Django"
+        )
+
+
+    def test_other_creation(self):
+        "Objects created on another database don't leak onto the default 
database"
+        # Create a book on the second database
+        Book.objects.using('other').create(title="Dive into Python",
+            published=datetime.date(2009, 5, 4))
+
+        # Create a book on the default database using a save
+        pro = Book()
+        pro.title="Pro Django"
+        pro.published = datetime.date(2008, 12, 16)
+        pro.save(using='other')
+
+        # Check that book exists on the default database, but not on other 
database
+        try:
+            Book.objects.using('other').get(title="Dive into Python")
+        except Book.DoesNotExist:
+            self.fail('"Dive Into Python" should exist on other database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.get,
+            title="Dive into Python"
+        )
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('default').get,
+            title="Dive into Python"
+        )
+
+        try:
+            Book.objects.using('other').get(title="Pro Django")
+        except Book.DoesNotExist:
+            self.fail('"Pro Django" should exist on other database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.get,
+            title="Pro Django"
+        )
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('default').get,
+            title="Pro Django"
+        )
+
     def test_basic_queries(self):
-        for db in connections:
-            self.assertRaises(Book.DoesNotExist,
-                lambda: Book.objects.using(db).get(title="Dive into Python"))
-            Book.objects.using(db).create(title="Dive into Python",
-                published=datetime.date(2009, 5, 4))
+        "Queries are constrained to a single database"
+        dive = Book.objects.using('other').create(title="Dive into Python",
+                                                  
published=datetime.date(2009, 5, 4))
 
-        for db in connections:
-            books = Book.objects.all().using(db)
-            self.assertEqual(books.count(), 1)
-            self.assertEqual(len(books), 1)
-            self.assertEqual(books[0].title, "Dive into Python")
-            self.assertEqual(books[0].published, datetime.date(2009, 5, 4))
+        dive =  Book.objects.using('other').get(published=datetime.date(2009, 
5, 4))
+        self.assertEqual(dive.title, "Dive into Python")
+        self.assertRaises(Book.DoesNotExist, 
Book.objects.using('default').get, published=datetime.date(2009, 5, 4))
 
-        for db in connections:
-            self.assertRaises(Book.DoesNotExist,
-                lambda: Book.objects.using(db).get(title="Pro Django"))
-            book = Book(title="Pro Django", published=datetime.date(2008, 12, 
16))
-            book.save(using=db)
+        dive = Book.objects.using('other').get(title__icontains="dive")
+        self.assertEqual(dive.title, "Dive into Python")
+        self.assertRaises(Book.DoesNotExist, 
Book.objects.using('default').get, title__icontains="dive")
 
-        for db in connections:
-            books = Book.objects.all().using(db)
-            self.assertEqual(books.count(), 2)
-            self.assertEqual(len(books), 2)
-            self.assertEqual(books[0].title, "Dive into Python")
-            self.assertEqual(books[1].title, "Pro Django")
+        dive = Book.objects.using('other').get(title__iexact="dive INTO 
python")
+        self.assertEqual(dive.title, "Dive into Python")
+        self.assertRaises(Book.DoesNotExist, 
Book.objects.using('default').get, title__iexact="dive INTO python")
 
-            pro = Book.objects.using(db).get(published=datetime.date(2008, 12, 
16))
-            self.assertEqual(pro.title, "Pro Django")
+        dive =  Book.objects.using('other').get(published__year=2009)
+        self.assertEqual(dive.title, "Dive into Python")
+        self.assertEqual(dive.published, datetime.date(2009, 5, 4))
+        self.assertRaises(Book.DoesNotExist, 
Book.objects.using('default').get, published__year=2009)
 
-            dive = Book.objects.using(db).get(title__icontains="dive")
-            self.assertEqual(dive.title, "Dive into Python")
+        years = Book.objects.using('other').dates('published', 'year')
+        self.assertEqual([o.year for o in years], [2009])
+        years = Book.objects.using('default').dates('published', 'year')
+        self.assertEqual([o.year for o in years], [])
 
-            dive = Book.objects.using(db).get(title__iexact="dive INTO python")
-            self.assertEqual(dive.title, "Dive into Python")
+        months = Book.objects.using('other').dates('published', 'month')
+        self.assertEqual([o.month for o in months], [5])
+        months = Book.objects.using('default').dates('published', 'month')
+        self.assertEqual([o.month for o in months], [])
 
-            pro = Book.objects.using(db).get(published__year=2008)
-            self.assertEqual(pro.title, "Pro Django")
-            self.assertEqual(pro.published, datetime.date(2008, 12, 16))
+    def test_m2m(self):
+        "M2M fields are constrained to a single database"
+        # Create a book and author on the default database
+        dive = Book.objects.create(title="Dive into Python",
+                                       published=datetime.date(2009, 5, 4))
 
-            years = Book.objects.using(db).dates('published', 'year')
-            self.assertEqual([o.year for o in years], [2008, 2009])
+        mark = Author.objects.create(name="Mark Pilgrim")
 
-            months = Book.objects.dates('published', 'month').using(db)
-            self.assertEqual(sorted(o.month for o in months), [5, 12])
+        # Create a book and author on the other database
+        pro = Book.objects.using('other').create(title="Pro Django",
+                                                       
published=datetime.date(2008, 12, 16))
 
+        marty = Author.objects.using('other').create(name="Marty Alchin")
+
+        # Save the author relations
+        dive.authors = [mark]
+        pro.authors = [marty]
+
+        # Inspect the m2m tables directly.
+        # There should be 1 entry in each database
+        
self.assertEquals(Book.authors.through.objects.using('default').count(), 1)
+        self.assertEquals(Book.authors.through.objects.using('other').count(), 
1)
+
+        # Check that queries work across m2m joins
+        
self.assertEquals(Book.objects.using('default').filter(authors__name='Mark 
Pilgrim').values_list('title', flat=True),
+                          ['Dive into Python'])
+        
self.assertEquals(Book.objects.using('other').filter(authors__name='Mark 
Pilgrim').values_list('title', flat=True),
+                          [])
+
+        
self.assertEquals(Book.objects.using('default').filter(authors__name='Marty 
Alchin').values_list('title', flat=True),
+                          [])
+        
self.assertEquals(Book.objects.using('other').filter(authors__name='Marty 
Alchin').values_list('title', flat=True),
+                          ['Pro Django'])
+
+    def test_foreign_key(self):
+        "FK fields are constrained to a single database"
+        # Create a book and author on the default database
+        dive = Book.objects.create(title="Dive into Python",
+                                       published=datetime.date(2009, 5, 4))
+
+        mark = Author.objects.create(name="Mark Pilgrim")
+
+        # Create a book and author on the other database
+        pro = Book.objects.using('other').create(title="Pro Django",
+                                                       
published=datetime.date(2008, 12, 16))
+
+        marty = Author.objects.using('other').create(name="Marty Alchin")
+
+        # Save the author's favourite books
+        mark.favourite_book = dive
+        mark.save()
+
+        marty.favourite_book = pro
+        marty.save() # FIXME Should this be save(using=alias)?
+
+        mark = Author.objects.using('default').get(name="Mark Pilgrim")
+        self.assertEquals(mark.favourite_book.title, "Dive into Python")
+
+        marty = Author.objects.using('other').get(name='Marty Alchin')
+        self.assertEquals(marty.favourite_book.title, "Dive into Python")
+
+        try:
+            mark.favourite_book = marty
+            self.fail("Shouldn't be able to assign across databases")
+        except Exception: # FIXME - this should be more explicit
+            pass
+
+        # Check that queries work across foreign key joins
+        
self.assertEquals(Book.objects.using('default').filter(favourite_of__name='Mark 
Pilgrim').values_list('title', flat=True),
+                          ['Dive into Python'])
+        
self.assertEquals(Book.objects.using('other').filter(favourite_of__name='Mark 
Pilgrim').values_list('title', flat=True),
+                          [])
+
+        
self.assertEquals(Book.objects.using('default').filter(favourite_of__name='Marty
 Alchin').values_list('title', flat=True),
+                          [])
+        
self.assertEquals(Book.objects.using('other').filter(favourite_of__name='Marty 
Alchin').values_list('title', flat=True),
+                          ['Pro Django'])
+
+class FixtureTestCase(TestCase):
+    multi_db = True
+    fixtures = ['multidb-common', 'multidb']
+
+    def test_fixture_loading(self):
+        "Multi-db fixtures are loaded correctly"
+        # Check that "Dive into Python" exists on the default database, but 
not on other database
+        try:
+            Book.objects.get(title="Dive into Python")
+            Book.objects.using('default').get(title="Dive into Python")
+        except Book.DoesNotExist:
+            self.fail('"Dive Into Python" should exist on default database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('other').get,
+            title="Dive into Python"
+        )
+
+        # Check that "Pro Django" exists on the default database, but not on 
other database
+        try:
+            Book.objects.using('other').get(title="Pro Django")
+        except Book.DoesNotExist:
+            self.fail('"Pro Django" should exist on other database')
+
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.get,
+            title="Pro Django"
+        )
+        self.assertRaises(Book.DoesNotExist,
+            Book.objects.using('default').get,
+            title="Pro Django"
+        )
+
+        # Check that "Definitive Guide" exists on the both databases
+        try:
+            Book.objects.get(title="The Definitive Guide to Django")
+            Book.objects.using('default').get(title="The Definitive Guide to 
Django")
+            Book.objects.using('other').get(title="The Definitive Guide to 
Django")
+        except Book.DoesNotExist:
+            self.fail('"The Definitive Guide to Django" should exist on both 
databases')
+
+
 class PickleQuerySetTestCase(TestCase):
+    multi_db = True
+
     def test_pickling(self):
         for db in connections:
+            Book.objects.using(db).create(title='Pro Django', 
published=datetime.date(2008, 12, 16))
             qs = Book.objects.all()
             self.assertEqual(qs._using, pickle.loads(pickle.dumps(qs))._using)
-
-
-if len(settings.DATABASES) > 1:
-    class MetaUsingTestCase(TestCase):
-        def test_meta_using_queries(self):
-            auth = Author.objects.create(name="Zed Shaw")
-            a = Article.objects.create(title="Django Rules!", author=auth)
-            self.assertEqual(Article.objects.get(title="Django Rules!"), a)
-            for db in connections:
-                if db == article_using:
-                    a1 = Article.objects.using(db).get(title="Django Rules!")
-                    self.assertEqual(a1, a)
-                    self.assertEqual(a1.author, auth)
-                else:
-                    self.assertRaises(Article.DoesNotExist,
-                        lambda: Article.objects.using(db).get(title="Django 
Rules!"))
-            a.delete()
-            self.assertRaises(Article.DoesNotExist,
-                lambda: Article.objects.get(title="Django Rules!"))
-            self.assertRaises(ValueError,
-                lambda: 
list(Article.objects.get(pk__in=Article.objects.using('default'))))

--

You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=.


Reply via email to