1
0
mirror of https://github.com/django/django.git synced 2025-11-07 07:15:35 +00:00

Fixed #1142 -- Added multiple database support.

This monster of a patch is the result of Alex Gaynor's 2009 Google Summer of Code project.
Congratulations to Alex for a job well done.

Big thanks also go to:
 * Justin Bronn for keeping GIS in line with the changes,
 * Karen Tracey and Jani Tiainen for their help testing Oracle support
 * Brett Hoerner, Jon Loyens, and Craig Kimmerer for their feedback.
 * Malcolm Treddinick for his guidance during the GSoC submission process.
 * Simon Willison for driving the original design process
 * Cal Henderson for complaining about ponies he wanted.

... and everyone else too numerous to mention that helped to bring this feature into fruition.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11952 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee
2009-12-22 15:18:51 +00:00
parent 7ef212af14
commit ff60c5f9de
231 changed files with 7860 additions and 5668 deletions

View File

@@ -16,7 +16,7 @@ try:
except NameError:
from django.utils.itercompat import sorted
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
class Article(models.Model):
headline = models.CharField(max_length=100, default='Default headline')
@@ -373,7 +373,7 @@ from django.conf import settings
building_docs = getattr(settings, 'BUILDING_DOCS', False)
if building_docs or settings.DATABASE_ENGINE == 'postgresql':
if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.postgresql':
__test__['API_TESTS'] += """
# In PostgreSQL, microsecond-level precision is available.
>>> a9 = Article(headline='Article 9', pub_date=datetime(2005, 7, 31, 12, 30, 45, 180))
@@ -382,7 +382,7 @@ if building_docs or settings.DATABASE_ENGINE == 'postgresql':
datetime.datetime(2005, 7, 31, 12, 30, 45, 180)
"""
if building_docs or settings.DATABASE_ENGINE == 'mysql':
if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.mysql':
__test__['API_TESTS'] += """
# In MySQL, microsecond-level precision isn't available. You'll lose
# microsecond-level precision once the data is saved.

View File

@@ -7,7 +7,7 @@ this behavior by explicitly adding ``primary_key=True`` to a field.
"""
from django.conf import settings
from django.db import models, transaction, IntegrityError
from django.db import models, transaction, IntegrityError, DEFAULT_DB_ALIAS
from fields import MyAutoField
@@ -156,7 +156,7 @@ True
# SQLite lets objects be saved with an empty primary key, even though an
# integer is expected. So we can't check for an error being raised in that case
# for SQLite. Remove it from the suite for this next bit.
if settings.DATABASE_ENGINE != 'sqlite3':
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
__test__["API_TESTS"] += """
# The primary key must be specified, so an error is raised if you try to create
# an object without it.

View File

@@ -171,9 +171,9 @@ True
# temporarily replace the UpdateQuery class to verify that E.f is actually nulled out first
>>> import django.db.models.sql
>>> class LoggingUpdateQuery(django.db.models.sql.UpdateQuery):
... def clear_related(self, related_field, pk_list):
... def clear_related(self, related_field, pk_list, using):
... print "CLEARING FIELD",related_field.name
... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list)
... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using)
>>> original_class = django.db.models.sql.UpdateQuery
>>> django.db.models.sql.UpdateQuery = LoggingUpdateQuery
>>> e1.delete()

View File

@@ -0,0 +1,10 @@
[
{
"pk": "6",
"model": "fixtures.article",
"fields": {
"headline": "Who needs more than one database?",
"pub_date": "2006-06-16 14:00:00"
}
}
]

View File

@@ -0,0 +1,10 @@
[
{
"pk": "8",
"model": "fixtures.article",
"fields": {
"headline": "There is no spoon.",
"pub_date": "2006-06-16 14:00:00"
}
}
]

View File

@@ -11,9 +11,10 @@ in the application directory, on in one of the directories named in the
from django.contrib.auth.models import Permission
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
from django.conf import settings
class Category(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
@@ -200,7 +201,7 @@ __test__ = {'API_TESTS': """
# Database flushing does not work on MySQL with the default storage engine
# because it requires transaction support.
if settings.DATABASE_ENGINE != 'mysql':
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
__test__['API_TESTS'] += \
"""
# Reset the database representation of this app. This will delete all data.
@@ -261,6 +262,41 @@ Multiple fixtures named 'fixture5' in '...fixtures'. Aborting.
>>> management.call_command('flush', verbosity=0, interactive=False)
# Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
>>> management.call_command('loaddata', 'db_fixture_1', verbosity=0)
>>> management.call_command('loaddata', 'db_fixture_2', verbosity=0)
>>> Article.objects.all()
[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
>>> management.call_command('flush', verbosity=0, interactive=False)
# Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
>>> management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default')
>>> management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default')
>>> Article.objects.all()
[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
>>> management.call_command('flush', verbosity=0, interactive=False)
# Try to load db fixture 3. This won't load because the database identifier doesn't match
>>> management.call_command('loaddata', 'db_fixture_3', verbosity=0)
>>> Article.objects.all()
[<Article: Python program becomes self aware>]
>>> management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default')
>>> Article.objects.all()
[<Article: Python program becomes self aware>]
>>> management.call_command('flush', verbosity=0, interactive=False)
# Try to load fixture 1, but this time, exclude the 'fixtures' app.
>>> management.call_command('loaddata', 'fixture1', verbosity=0, exclude='fixtures')
>>> Article.objects.all()
[<Article: Python program becomes self aware>]
>>> Category.objects.all()
[]
# Load back in fixture 1, we need the articles from it
>>> management.call_command('loaddata', 'fixture1', verbosity=0)

View File

@@ -4,7 +4,7 @@
This demonstrates features of the database API.
"""
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
from django.conf import settings
class Article(models.Model):
@@ -43,7 +43,9 @@ False
True
"""}
if settings.DATABASE_ENGINE in ('postgresql', 'postgresql_pysycopg2'):
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] in (
'django.db.backends.postgresql',
'django.db.backends.postgresql_pysycopg2'):
__test__['API_TESTS'] += r"""
# text matching tests for PostgreSQL 8.3
>>> Article.objects.filter(id__iexact='1')
@@ -405,7 +407,7 @@ FieldError: Join on field 'headline' not permitted. Did you misspell 'starts' fo
"""
if settings.DATABASE_ENGINE != 'mysql':
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
__test__['API_TESTS'] += r"""
# grouping and backreferences
>>> Article.objects.filter(headline__regex=r'b(.).*b\1')

View File

@@ -157,7 +157,7 @@ False
# The underlying query only makes one join when a related table is referenced twice.
>>> queryset = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
>>> sql = queryset.query.as_sql()[0]
>>> sql = queryset.query.get_compiler(queryset.db).as_sql()[0]
>>> sql.count('INNER JOIN')
1

View File

@@ -166,12 +166,13 @@ class ProxyImprovement(Improvement):
__test__ = {'API_TESTS' : """
# The MyPerson model should be generating the same database queries as the
# Person model (when the same manager is used in each case).
>>> MyPerson.other.all().query.as_sql() == Person.objects.order_by("name").query.as_sql()
>>> from django.db import DEFAULT_DB_ALIAS
>>> MyPerson.other.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql() == Person.objects.order_by("name").query.get_compiler(DEFAULT_DB_ALIAS).as_sql()
True
# The StatusPerson models should have its own table (it's using ORM-level
# inheritance).
>>> StatusPerson.objects.all().query.as_sql() == Person.objects.all().query.as_sql()
>>> StatusPerson.objects.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql() == Person.objects.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql()
False
# Creating a Person makes them accessible through the MyPerson proxy.

View File

@@ -10,7 +10,7 @@ class RawQueryTests(TestCase):
"""
Execute the passed query against the passed model and check the output
"""
results = list(model.objects.raw(query=query, params=params, translations=translations))
results = list(model.objects.raw(query, params=params, translations=translations))
self.assertProcessed(results, expected_results, expected_annotations)
self.assertAnnotations(results, expected_annotations)
@@ -111,7 +111,7 @@ class RawQueryTests(TestCase):
query = "SELECT * FROM raw_query_author WHERE first_name = %s"
author = Author.objects.all()[2]
params = [author.first_name]
results = list(Author.objects.raw(query=query, params=params))
results = list(Author.objects.raw(query, params=params))
self.assertProcessed(results, [author])
self.assertNoAnnotations(results)
self.assertEqual(len(results), 1)

View File

@@ -149,8 +149,8 @@ __test__ = {'API_TESTS':"""
# database since the data was serialized (we'll simulate that below).
>>> for obj in serializers.deserialize("xml", xml):
... print obj
<DeserializedObject: Poker has no place on ESPN>
<DeserializedObject: Time to reform copyright>
<DeserializedObject: serializers.Article(pk=1)>
<DeserializedObject: serializers.Article(pk=2)>
# Deserializing data with different field values doesn't change anything in the
# database until we call save():
@@ -183,8 +183,8 @@ __test__ = {'API_TESTS':"""
>>> json = serializers.serialize("json", Article.objects.all())
>>> for obj in serializers.deserialize("json", json):
... print obj
<DeserializedObject: Poker has no place on television>
<DeserializedObject: Time to reform copyright>
<DeserializedObject: serializers.Article(pk=1)>
<DeserializedObject: serializers.Article(pk=2)>
>>> json = json.replace("Poker has no place on television", "Just kidding; I love TV poker")
>>> for obj in serializers.deserialize("json", json):
@@ -205,7 +205,7 @@ __test__ = {'API_TESTS':"""
>>> for obj in serializers.deserialize("json", json):
... print obj
<DeserializedObject: Profile of Joe>
<DeserializedObject: serializers.AuthorProfile(pk=1)>
# Objects ids can be referenced before they are defined in the serialization data
# However, the deserialization process will need to be contained within a transaction
@@ -275,7 +275,7 @@ None
>>> obj = list(serializers.deserialize("json", serialized))[0]
>>> print obj
<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
<DeserializedObject: serializers.Player(pk=1)>
"""}
@@ -310,8 +310,8 @@ try:
>>> obs = list(serializers.deserialize("yaml", serialized))
>>> for i in obs:
... print i
<DeserializedObject: Just kidding; I love TV poker>
<DeserializedObject: Time to reform copyright>
<DeserializedObject: serializers.Article(pk=1)>
<DeserializedObject: serializers.Article(pk=2)>
# Custom field with non trivial to string convertion value with YAML serializer
@@ -324,7 +324,7 @@ try:
>>> serialized = serializers.serialize("yaml", Player.objects.all())
>>> obj = list(serializers.deserialize("yaml", serialized))[0]
>>> print obj
<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
<DeserializedObject: serializers.Player(pk=1)>
"""

View File

@@ -7,7 +7,7 @@ commit-on-success behavior. Alternatively, you can manage the transaction
manually.
"""
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
@@ -28,7 +28,7 @@ from django.conf import settings
building_docs = getattr(settings, 'BUILDING_DOCS', False)
if building_docs or settings.DATABASE_ENGINE != 'mysql':
if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
__test__['API_TESTS'] += """
# the default behavior is to autocommit after each save() action
>>> def create_a_reporter_then_fail(first, last):