1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

[soc2009/multidb] Add multi-db support to fixture loading. Involves a rework of models in the multi-db test cases to make the fixture a complete test case. Patch from Russell Keith-Magee.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11891 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-12-17 16:12:43 +00:00
parent 3c8c3bd651
commit 44f21f2374
14 changed files with 195 additions and 126 deletions

View File

@ -48,8 +48,8 @@ class SiteProfileNotAvailable(Exception):
pass pass
class PermissionManager(models.Manager): class PermissionManager(models.Manager):
def get_by_natural_key(self, codename, app_label, model): def get_by_natural_key(self, codename, app_label, model, using=DEFAULT_DB_ALIAS):
return self.get( return self.using(using).get(
codename=codename, codename=codename,
content_type=ContentType.objects.get_by_natural_key(app_label, model) content_type=ContentType.objects.get_by_natural_key(app_label, model)
) )

View File

@ -159,7 +159,7 @@ class Command(BaseCommand):
print "Installing %s fixture '%s' from %s." % \ print "Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir)) (format, fixture_name, humanize(fixture_dir))
try: try:
objects = serializers.deserialize(format, fixture) objects = serializers.deserialize(format, fixture, using=using)
for obj in objects: for obj in objects:
if obj.object._meta.app_label not in excluded_apps: if obj.object._meta.app_label not in excluded_apps:
objects_in_fixture += 1 objects_in_fixture += 1

View File

@ -36,14 +36,14 @@ except ImportError:
_serializers = {} _serializers = {}
def register_serializer(format, serializer_module, serializers=None): 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 ``serializer_module`` should be the fully qualified module name
for the serializer. for the serializer.
If ``serializers`` is provided, the registration will be added If ``serializers`` is provided, the registration will be added
to the provided dictionary. to the provided dictionary.
If ``serializers`` is not provided, the registration will be made If ``serializers`` is not provided, the registration will be made
directly into the global register of serializers. Adding serializers directly into the global register of serializers. Adding serializers
directly is not a thread-safe operation. directly is not a thread-safe operation.
@ -53,7 +53,7 @@ def register_serializer(format, serializer_module, serializers=None):
_serializers[format] = module _serializers[format] = module
else: else:
serializers[format] = module serializers[format] = module
def unregister_serializer(format): def unregister_serializer(format):
"Unregister a given serializer. This is not a thread-safe operation." "Unregister a given serializer. This is not a thread-safe operation."
del _serializers[format] del _serializers[format]
@ -87,7 +87,7 @@ def serialize(format, queryset, **options):
s.serialize(queryset, **options) s.serialize(queryset, **options)
return s.getvalue() 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, Deserialize a stream or a string. Returns an iterator that yields ``(obj,
m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* -- m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* --
@ -95,7 +95,7 @@ def deserialize(format, stream_or_string):
list_of_related_objects}``. list_of_related_objects}``.
""" """
d = get_deserializer(format) d = get_deserializer(format)
return d(stream_or_string) return d(stream_or_string, **options)
def _load_serializers(): def _load_serializers():
""" """

View File

@ -39,7 +39,7 @@ def Deserializer(stream_or_string, **options):
stream = StringIO(stream_or_string) stream = StringIO(stream_or_string)
else: else:
stream = stream_or_string stream = stream_or_string
for obj in PythonDeserializer(simplejson.load(stream)): for obj in PythonDeserializer(simplejson.load(stream), **options):
yield obj yield obj
class DjangoJSONEncoder(simplejson.JSONEncoder): class DjangoJSONEncoder(simplejson.JSONEncoder):

View File

@ -6,7 +6,7 @@ other serializers.
from django.conf import settings from django.conf import settings
from django.core.serializers import base 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 from django.utils.encoding import smart_unicode, is_protected_type
class Serializer(base.Serializer): 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 It's expected that you pass the Python objects themselves (instead of a
stream or a string) to the constructor stream or a string) to the constructor
""" """
db = options.pop('using', DEFAULT_DB_ALIAS)
models.get_apps() models.get_apps()
for d in object_list: for d in object_list:
# Look up the model and starting build a dict of data for it. # 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'): if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
def m2m_convert(value): def m2m_convert(value):
if hasattr(value, '__iter__'): 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: else:
return smart_unicode(field.rel.to._meta.pk.to_python(value)) return smart_unicode(field.rel.to._meta.pk.to_python(value))
else: else:
@ -108,7 +109,7 @@ def Deserializer(object_list, **options):
if field_value is not None: if field_value is not None:
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
if hasattr(field_value, '__iter__'): 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) value = getattr(obj, field.rel.field_name)
else: else:
value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)

View File

@ -58,6 +58,6 @@ def Deserializer(stream_or_string, **options):
stream = StringIO(stream_or_string) stream = StringIO(stream_or_string)
else: else:
stream = stream_or_string stream = stream_or_string
for obj in PythonDeserializer(yaml.load(stream)): for obj in PythonDeserializer(yaml.load(stream), **options):
yield obj yield obj

View File

@ -4,7 +4,7 @@ XML serializer.
from django.conf import settings from django.conf import settings
from django.core.serializers import base 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.xmlutils import SimplerXMLGenerator
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from xml.dom import pulldom from xml.dom import pulldom
@ -149,6 +149,7 @@ class Deserializer(base.Deserializer):
def __init__(self, stream_or_string, **options): def __init__(self, stream_or_string, **options):
super(Deserializer, self).__init__(stream_or_string, **options) super(Deserializer, self).__init__(stream_or_string, **options)
self.event_stream = pulldom.parse(self.stream) self.event_stream = pulldom.parse(self.stream)
self.db = options.pop('using', DEFAULT_DB_ALIAS)
def next(self): def next(self):
for event, node in self.event_stream: for event, node in self.event_stream:
@ -218,7 +219,7 @@ class Deserializer(base.Deserializer):
if keys: if keys:
# If there are 'natural' subelements, it must be a natural key # If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys] 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) obj_pk = getattr(obj, field.rel.field_name)
else: else:
# Otherwise, treat like a normal PK # Otherwise, treat like a normal PK
@ -239,7 +240,7 @@ class Deserializer(base.Deserializer):
if keys: if keys:
# If there are 'natural' subelements, it must be a natural key # If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys] 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: else:
# Otherwise, treat like a normal PK value. # Otherwise, treat like a normal PK value.
obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk')) obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk'))

View File

@ -614,7 +614,7 @@ class Model(object):
param = smart_str(getattr(self, field.attname)) param = smart_str(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param}) q = Q(**{'%s__%s' % (field.name, op): param})
q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk}) 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: try:
return qs[0] return qs[0]
except IndexError: except IndexError:

View File

@ -57,8 +57,8 @@ class Tag(models.Model):
self.tagged, self.name) self.tagged, self.name)
class PersonManager(models.Manager): class PersonManager(models.Manager):
def get_by_natural_key(self, name): def get_by_natural_key(self, name, using=DEFAULT_DB_ALIAS):
return self.get(name=name) return self.using(using).get(name=name)
class Person(models.Model): class Person(models.Model):
objects = PersonManager() objects = PersonManager()

View File

@ -83,8 +83,8 @@ class WidgetProxy(Widget):
# Check for forward references in FKs and M2Ms with natural keys # Check for forward references in FKs and M2Ms with natural keys
class TestManager(models.Manager): class TestManager(models.Manager):
def get_by_natural_key(self, key): def get_by_natural_key(self, key, using=DEFAULT_DB_ALIAS):
return self.get(name=key) return self.using(using).get(name=key)
class Store(models.Model): class Store(models.Model):
objects = TestManager() objects = TestManager()

View File

@ -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, "pk": 2,
"model": "multiple_database.book", "model": "multiple_database.book",
"fields": { "fields": {
"title": "Pro Django", "title": "Pro Django",
"published": "2008-12-16" "published": "2008-12-16",
"authors": [["Marty Alchin"]],
"editor": ["George Vilches"]
} }
} }
] ]

View File

@ -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, "pk": 2,
"model": "multiple_database.book", "model": "multiple_database.book",
"fields": { "fields": {
"title": "Dive into Python", "title": "Dive into Python",
"published": "2009-5-4" "published": "2009-5-4",
"authors": [["Mark Pilgrim"]],
"editor": ["Chris Mills"]
} }
} }
] ]

View File

@ -16,10 +16,25 @@ class Review(models.Model):
class Meta: class Meta:
ordering = ('source',) 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): class Book(models.Model):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
published = models.DateField() published = models.DateField()
authors = models.ManyToManyField('Author') authors = models.ManyToManyField(Person)
editor = models.ForeignKey(Person, null=True, related_name='edited')
reviews = generic.GenericRelation(Review) reviews = generic.GenericRelation(Review)
def __unicode__(self): def __unicode__(self):
@ -28,16 +43,6 @@ class Book(models.Model):
class Meta: class Meta:
ordering = ('title',) 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): class UserProfile(models.Model):
user = models.OneToOneField(User) user = models.OneToOneField(User)
flavor = models.CharField(max_length=100) flavor = models.CharField(max_length=100)

View File

@ -6,7 +6,7 @@ from django.contrib.auth.models import User
from django.db import connections from django.db import connections
from django.test import TestCase from django.test import TestCase
from models import Book, Author, Review, UserProfile from models import Book, Person, Review, UserProfile
try: try:
# we only have these models if the user is using multi-db, it's safe the # 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", pro = Book.objects.create(title="Pro Django",
published=datetime.date(2008, 12, 16)) 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 # Create a book and author on the other database
dive = Book.objects.using('other').create(title="Dive into Python", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Save the author relations
pro.authors = [marty] pro.authors = [marty]
@ -163,7 +163,7 @@ class QueryTestCase(TestCase):
# Reget the objects to clear caches # Reget the objects to clear caches
dive = Book.objects.using('other').get(title="Dive into Python") 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 # Retrive related object by descriptor. Related objects should be database-baound
self.assertEquals(list(dive.authors.all().values_list('name', flat=True)), 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", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Save the author relations
dive.authors = [mark] dive.authors = [mark]
# Add a second author # 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)), 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", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Save the author relations
dive.authors = [mark] dive.authors = [mark]
@ -233,30 +233,30 @@ class QueryTestCase(TestCase):
# Add a books to the m2m # Add a books to the m2m
mark.book_set.add(grease) 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']) [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']) [u'Mark Pilgrim'])
# Remove a book from the m2m # Remove a book from the m2m
mark.book_set.remove(grease) 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']) [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 # Clear the books associated with mark
mark.book_set.clear() 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 # Create a book through the m2m interface
mark.book_set.create(title="Dive into HTML5", published=datetime.date(2020, 1, 1)) 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']) [u'Mark Pilgrim'])
def test_m2m_cross_database_protection(self): def test_m2m_cross_database_protection(self):
@ -265,13 +265,13 @@ class QueryTestCase(TestCase):
pro = Book.objects.create(title="Pro Django", pro = Book.objects.create(title="Pro Django",
published=datetime.date(2008, 12, 16)) 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 # Create a book and author on the other database
dive = Book.objects.using('other').create(title="Dive into Python", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Set a foreign key set with an object from a different database
try: try:
marty.book_set = [pro, dive] marty.book_set = [pro, dive]
@ -313,91 +313,93 @@ class QueryTestCase(TestCase):
pro = Book.objects.create(title="Pro Django", pro = Book.objects.create(title="Pro Django",
published=datetime.date(2008, 12, 16)) 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 # Create a book and author on the other database
dive = Book.objects.using('other').create(title="Dive into Python", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Save the author's favourite books
marty.favourite_book = pro pro.editor = george
marty.save() pro.save()
mark.favourite_book = dive dive.editor = chris
mark.save() dive.save()
marty = Author.objects.using('default').get(name="Marty Alchin") pro = Book.objects.using('default').get(title="Pro Django")
self.assertEquals(marty.favourite_book.title, "Pro Django") self.assertEquals(pro.editor.name, "George Vilches")
mark = Author.objects.using('other').get(name='Mark Pilgrim') dive = Book.objects.using('other').get(title="Dive into Python")
self.assertEquals(mark.favourite_book.title, "Dive into Python") self.assertEquals(dive.editor.name, "Chris Mills")
# Check that queries work across foreign key joins # 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)), self.assertEquals(list(Person.objects.using('default').filter(edited__title='Pro Django').values_list('name', flat=True)),
[u'Pro Django']) [u'George Vilches'])
self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Marty Alchin').values_list('title', flat=True)), 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)), self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
[u'Dive into Python']) [u'Chris Mills'])
# Reget the objects to clear caches # 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") 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 # Retrive related object by descriptor. Related objects should be database-baound
self.assertEquals(list(dive.favourite_of.all().values_list('name', flat=True)), self.assertEquals(list(chris.edited.values_list('title', flat=True)),
[u'Mark Pilgrim']) [u'Dive into Python'])
self.assertEquals(mark.favourite_book.title, u'Dive into Python')
def test_foreign_key_reverse_operations(self): def test_foreign_key_reverse_operations(self):
"FK reverse manipulations are all constrained to a single DB" "FK reverse manipulations are all constrained to a single DB"
dive = Book.objects.using('other').create(title="Dive into Python", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Save the author relations
mark.favourite_book = dive dive.editor = chris
mark.save() dive.save()
# Add a second author # Add a second book edited by chris
john = Author.objects.using('other').create(name="John Smith") html5 = Book.objects.using('other').create(title="Dive into HTML5", published=datetime.date(2010, 3, 15))
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 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) # Remove the second editor
self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), chris.edited.remove(html5)
[u'Dive into Python']) 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)),
[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)),
[]) [])
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 # Clear all edited books
dive.favourite_of.clear() chris.edited.clear()
self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), 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 # Create an author through the m2m interface
dive.favourite_of.create(name='Jane Brown') chris.edited.create(title='Dive into Water', published=datetime.date(2010, 3, 15))
self.assertEquals(list(Book.objects.using('other').filter(favourite_of__name='Mark Pilgrim').values_list('title', flat=True)), 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): def test_foreign_key_cross_database_protection(self):
"Operations that involve sharing FK objects across databases raise an error" "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", pro = Book.objects.create(title="Pro Django",
published=datetime.date(2008, 12, 16)) 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 # Create a book and author on the other database
dive = Book.objects.using('other').create(title="Dive into Python", dive = Book.objects.using('other').create(title="Dive into Python",
published=datetime.date(2009, 5, 4)) 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 # Set a foreign key with an object from a different database
try: try:
marty.favourite_book = dive dive.editor = marty
self.fail("Shouldn't be able to assign across databases") self.fail("Shouldn't be able to assign across databases")
except ValueError: except ValueError:
pass pass
# Set a foreign key set with an object from a different database # Set a foreign key set with an object from a different database
try: try:
dive.favourite_of = [mark, marty] marty.edited = [pro, dive]
self.fail("Shouldn't be able to assign across databases") self.fail("Shouldn't be able to assign across databases")
except ValueError: except ValueError:
pass pass
# Add to a foreign key set with an object from a different database # Add to a foreign key set with an object from a different database
try: try:
dive.favourite_of.add(marty) marty.edited.add(dive)
self.fail("Shouldn't be able to assign across databases") self.fail("Shouldn't be able to assign across databases")
except ValueError: except ValueError:
pass pass
@ -437,37 +439,50 @@ class QueryTestCase(TestCase):
# BUT! if you assign a FK object when the base object hasn't # BUT! if you assign a FK object when the base object hasn't
# been saved yet, you implicitly assign the database for the # been saved yet, you implicitly assign the database for the
# base object. # 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 # 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'... # old object comes from 'other', so the new object is set to use 'other'...
john.favourite_book = dive dive.editor = chris
self.assertEquals(john._state.db, 'other') html5.editor = mark
# self.assertEquals(chris._state.db, 'other')
self.assertEquals(html5._state.db, 'other')
# ... but it isn't saved yet # ... 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']) [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' # When saved (no using required), new objects goes to 'other'
john.save() chris.save()
self.assertEquals(list(Author.objects.using('default').values_list('name',flat=True)), html5.save()
self.assertEquals(list(Person.objects.using('default').values_list('name',flat=True)),
[u'Marty Alchin']) [u'Marty Alchin'])
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'John Smith', u'Mark Pilgrim']) [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 # This also works if you assign the FK in the constructor
jane = Author(name='Jane Brown', favourite_book=dive) water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark)
self.assertEquals(jane._state.db, 'other') self.assertEquals(water._state.db, 'other')
# ... but it isn't saved yet # ... but it isn't saved yet
self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),
[u'John Smith', u'Mark Pilgrim']) [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' # When saved, the new book goes to 'other'
jane.save() water.save()
self.assertEquals(list(Author.objects.using('default').values_list('name',flat=True)), self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),
[u'Marty Alchin']) [u'Pro Django'])
self.assertEquals(list(Author.objects.using('other').values_list('name',flat=True)), self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),
[u'Jane Brown', u'John Smith', u'Mark Pilgrim']) [u'Dive into HTML5', u'Dive into Python', u'Dive into Water'])
def test_generic_key_separation(self): def test_generic_key_separation(self):
"Generic fields are constrained to a single database" "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)), self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)),
[u'Python Daily', u'Python Weekly']) [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): class UserProfileTestCase(TestCase):
def setUp(self): def setUp(self):
self.old_auth_profile_module = getattr(settings, 'AUTH_PROFILE_MODULE', None) 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.get(title="Pro Django")
Book.objects.using('default').get(title="Pro Django") Book.objects.using('default').get(title="Pro Django")
except Book.DoesNotExist: 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, self.assertRaises(Book.DoesNotExist,
Book.objects.using('other').get, Book.objects.using('other').get,