Ticket #12540: t12540-r12262.1.diff
File t12540-r12262.1.diff, 46.2 KB (added by , 15 years ago) |
---|
-
django/contrib/auth/models.py
diff -r e165fea06e06 django/contrib/auth/models.py
a b 3 3 4 4 from django.contrib import auth 5 5 from django.core.exceptions import ImproperlyConfigured 6 from django.db import models , DEFAULT_DB_ALIAS6 from django.db import models 7 7 from django.db.models.manager import EmptyManager 8 8 from django.contrib.contenttypes.models import ContentType 9 9 from django.utils.encoding import smart_str -
django/contrib/contenttypes/generic.py
diff -r e165fea06e06 django/contrib/contenttypes/generic.py
a b 5 5 from django.core.exceptions import ObjectDoesNotExist 6 6 from django.db import connection 7 7 from django.db.models import signals 8 from django.db import models , DEFAULT_DB_ALIAS8 from django.db import models 9 9 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel 10 10 from django.db.models.loading import get_model 11 11 from django.forms import ModelForm … … 255 255 raise TypeError("'%s' instance expected" % self.model._meta.object_name) 256 256 setattr(obj, self.content_type_field_name, self.content_type) 257 257 setattr(obj, self.object_id_field_name, self.pk_val) 258 obj.save( using=self.instance._state.db)258 obj.save() 259 259 add.alters_data = True 260 260 261 261 def remove(self, *objs): -
django/contrib/contenttypes/models.py
diff -r e165fea06e06 django/contrib/contenttypes/models.py
a b 1 from django.db import models , DEFAULT_DB_ALIAS1 from django.db import models 2 2 from django.utils.translation import ugettext_lazy as _ 3 3 from django.utils.encoding import smart_unicode 4 4 -
django/contrib/gis/db/models/sql/query.py
diff -r e165fea06e06 django/contrib/gis/db/models/sql/query.py
a b 1 from django.db import connections , DEFAULT_DB_ALIAS1 from django.db import connections 2 2 from django.db.models.query import sql 3 3 4 4 from django.contrib.gis.db.models.fields import GeometryField -
django/db/models/base.py
diff -r e165fea06e06 django/db/models/base.py
a b 439 439 need for overrides of save() to pass around internal-only parameters 440 440 ('raw', 'cls', and 'origin'). 441 441 """ 442 using = using or self._ state.db or DEFAULT_DB_ALIAS442 using = using or self._default_manager._db_for_write(self) 443 443 connection = connections[using] 444 444 assert not (force_insert and force_update) 445 445 if cls is None: … … 592 592 parent_obj._collect_sub_objects(seen_objs) 593 593 594 594 def delete(self, using=None): 595 using = using or self._ state.db or DEFAULT_DB_ALIAS595 using = using or self._default_manager._db_for_write(self) 596 596 connection = connections[using] 597 597 assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname) 598 598 … … 719 719 # no value, skip the lookup 720 720 continue 721 721 if f.primary_key and not getattr(self, '_adding', False): 722 # no need to check for unique primary key when edit ting722 # no need to check for unique primary key when editing 723 723 continue 724 724 lookup_kwargs[str(field_name)] = lookup_value 725 725 -
django/db/models/fields/related.py
diff -r e165fea06e06 django/db/models/fields/related.py
a b 1 from django.conf import settings 1 2 from django.db import connection, transaction, DEFAULT_DB_ALIAS 2 3 from django.db.backends import util 3 4 from django.db.models import signals, get_model … … 218 219 raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' % 219 220 (value, instance._meta.object_name, 220 221 self.related.get_accessor_name(), self.related.opts.object_name)) 222 elif value is not None: 223 if instance._state.db is None: 224 instance._state.db = instance._default_manager._db_for_write(value) 225 elif value._state.db is None: 226 value._state.db = value._default_manager._db_for_write(instance) 227 elif value._state.db is not None and instance._state.db is not None: 228 if (settings.DATABASES[value._default_manager._db_for_write(instance)]['SOURCE'] 229 != settings.DATABASES[value._state.db]['SOURCE']): 230 raise ValueError('Cannot assign "%r": instance is on database "%s", value is is on database "%s"' % 231 (value, instance._state.db, value._state.db)) 221 232 222 233 # Set the value of the related field to the value of the related object's related field 223 234 setattr(value, self.related.field.attname, getattr(instance, self.related.field.rel.get_related_field().attname)) … … 260 271 # If the related manager indicates that it should be used for 261 272 # related fields, respect that. 262 273 rel_mgr = self.field.rel.to._default_manager 263 using = instance._state.db or DEFAULT_DB_ALIAS274 using = rel_mgr._db_for_read(instance) 264 275 if getattr(rel_mgr, 'use_for_related_fields', False): 265 276 rel_obj = rel_mgr.using(using).get(**params) 266 277 else: … … 281 292 raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' % 282 293 (value, instance._meta.object_name, 283 294 self.field.name, self.field.rel.to._meta.object_name)) 284 elif value is not None and value._state.db != instance._state.db:295 elif value is not None: 285 296 if instance._state.db is None: 286 instance._state.db = value._state.db 287 else:#elif value._state.db is None: 288 value._state.db = instance._state.db 289 # elif value._state.db is not None and instance._state.db is not None: 290 # raise ValueError('Cannot assign "%r": instance is on database "%s", value is is on database "%s"' % 291 # (value, instance._state.db, value._state.db)) 297 instance._state.db = instance._default_manager._db_for_write(value) 298 elif value._state.db is None: 299 value._state.db = value._default_manager._db_for_write(instance) 300 elif value._state.db is not None and instance._state.db is not None: 301 if (settings.DATABASES[value._default_manager._db_for_write(instance)]['SOURCE'] 302 != settings.DATABASES[value._state.db]['SOURCE']): 303 raise ValueError('Cannot assign "%r": instance is on database "%s", value is is on database "%s"' % 304 (value, instance._state.db, value._state.db)) 292 305 293 306 # If we're setting the value of a OneToOneField to None, we need to clear 294 307 # out the cache on any old related object. Otherwise, deleting the … … 370 383 371 384 class RelatedManager(superclass): 372 385 def get_query_set(self): 373 using = instance._state.db or DEFAULT_DB_ALIAS386 using = rel_model._default_manager._db_for_read(instance) 374 387 return superclass.get_query_set(self).using(using).filter(**(self.core_filters)) 375 388 376 389 def add(self, *objs): … … 378 391 if not isinstance(obj, self.model): 379 392 raise TypeError("'%s' instance expected" % self.model._meta.object_name) 380 393 setattr(obj, rel_field.name, instance) 381 obj.save( using=instance._state.db)394 obj.save() 382 395 add.alters_data = True 383 396 384 397 def create(self, **kwargs): … … 390 403 # Update kwargs with the related object that this 391 404 # ForeignRelatedObjectsDescriptor knows about. 392 405 kwargs.update({rel_field.name: instance}) 393 using = instance._state.db or DEFAULT_DB_ALIAS406 using = rel_model._default_manager._db_for_write(instance) 394 407 return super(RelatedManager, self).using(using).get_or_create(**kwargs) 395 408 get_or_create.alters_data = True 396 409 … … 402 415 # Is obj actually part of this descriptor set? 403 416 if getattr(obj, rel_field.attname) == val: 404 417 setattr(obj, rel_field.name, None) 405 obj.save( using=instance._state.db)418 obj.save() 406 419 else: 407 420 raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, instance)) 408 421 remove.alters_data = True … … 410 423 def clear(self): 411 424 for obj in self.all(): 412 425 setattr(obj, rel_field.name, None) 413 obj.save( using=instance._state.db)426 obj.save() 414 427 clear.alters_data = True 415 428 416 429 manager = RelatedManager() … … 505 518 new_ids = set() 506 519 for obj in objs: 507 520 if isinstance(obj, self.model): 508 # if obj._state.db != self.instance._state.db: 509 # raise ValueError('Cannot add "%r": instance is on database "%s", value is is on database "%s"' % 510 # (obj, self.instance._state.db, obj._state.db)) 521 if (settings.DATABASES[obj._default_manager._db_for_write(self.instance)]['SOURCE'] 522 != settings.DATABASES[obj._state.db]['SOURCE']): 523 raise ValueError('Cannot add "%r": instance is on database "%s", value is is on database "%s"' % 524 (obj, self.instance._state.db, obj._state.db)) 511 525 new_ids.add(obj.pk) 512 526 elif isinstance(obj, Model): 513 527 raise TypeError("'%s' instance expected" % self.model._meta.object_name) 514 528 else: 515 529 new_ids.add(obj) 516 vals = self.through._default_manager.using(self.instance._state.db).values_list(target_field_name, flat=True) 530 db = self.instance._default_manager._db_for_read(self.instance) 531 vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True) 517 532 vals = vals.filter(**{ 518 533 source_field_name: self._pk_val, 519 534 '%s__in' % target_field_name: new_ids, 520 535 }) 521 536 new_ids = new_ids - set(vals) 522 537 # Add the ones that aren't there already 538 db = self.instance._default_manager._db_for_write(self.instance) 523 539 for obj_id in new_ids: 524 self.through._default_manager.using( self.instance._state.db).create(**{540 self.through._default_manager.using(db).create(**{ 525 541 '%s_id' % source_field_name: self._pk_val, 526 542 '%s_id' % target_field_name: obj_id, 527 543 }) -
django/db/models/manager.py
diff -r e165fea06e06 django/db/models/manager.py
a b 1 1 from django.utils import copycompat as copy 2 2 from django.conf import settings 3 3 from django.db import DEFAULT_DB_ALIAS 4 4 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query, RawQuerySet 5 5 from django.db.models import signals … … 87 87 mgr._inherited = True 88 88 return mgr 89 89 90 def db_manager(self, alias):90 def db_manager(self, using): 91 91 obj = copy.copy(self) 92 obj._db = alias92 obj._db = using 93 93 return obj 94 94 95 95 @property 96 96 def db(self): 97 return self._db or DEFAULT_DB_ALIAS 97 return self._db 98 99 def _db_for_write(self, instance): 100 if instance._state.db: 101 return settings.DATABASES[instance._state.db]['SOURCE'] or DEFAULT_DB_ALIAS 102 return DEFAULT_DB_ALIAS 103 104 def _db_for_read(self, instance): 105 return instance._state.db or DEFAULT_DB_ALIAS 98 106 99 107 ####################### 100 108 # PROXIES TO QUERYSET # 101 109 ####################### 102 110 103 111 def get_empty_query_set(self): 104 return EmptyQuerySet(self.model )112 return EmptyQuerySet(self.model, using=self.db) 105 113 106 114 def get_query_set(self): 107 115 """Returns a new QuerySet object. Subclasses can override this method 108 116 to easily customize the behavior of the Manager. 109 117 """ 110 qs = QuerySet(self.model) 111 if self._db is not None: 112 qs = qs.using(self._db) 113 return qs 118 return QuerySet(self.model, using=self.db) 114 119 115 120 def none(self): 116 121 return self.get_empty_query_set() -
django/db/models/query.py
diff -r e165fea06e06 django/db/models/query.py
a b 988 988 989 989 990 990 class EmptyQuerySet(QuerySet): 991 def __init__(self, model=None, query=None ):992 super(EmptyQuerySet, self).__init__(model, query )991 def __init__(self, model=None, query=None, using=None): 992 super(EmptyQuerySet, self).__init__(model, query, using) 993 993 self._result_cache = [] 994 994 995 995 def __and__(self, other): -
django/db/utils.py
diff -r e165fea06e06 django/db/utils.py
a b 55 55 conn = self.databases[alias] 56 56 except KeyError: 57 57 raise ConnectionDoesNotExist("The connection %s doesn't exist" % alias) 58 58 59 conn.setdefault('ENGINE', 'django.db.backends.dummy') 59 60 if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']: 60 61 conn['ENGINE'] = 'django.db.backends.dummy' … … 65 66 conn.setdefault('TIME_ZONE', settings.TIME_ZONE) 66 67 for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): 67 68 conn.setdefault(setting, '') 69 conn.setdefault('SOURCE', alias) 68 70 69 71 def __getitem__(self, alias): 70 72 if alias in self._connections: -
tests/regressiontests/multiple_database/tests.py
diff -r e165fea06e06 tests/regressiontests/multiple_database/tests.py
a b 259 259 self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into HTML5').values_list('name', flat=True)), 260 260 [u'Mark Pilgrim']) 261 261 262 #def test_m2m_cross_database_protection(self):263 #"Operations that involve sharing M2M objects across databases raise an error"264 ## Create a book and author on the default database265 #pro = Book.objects.create(title="Pro Django",266 #published=datetime.date(2008, 12, 16))262 def test_m2m_cross_database_protection(self): 263 "Operations that involve sharing M2M objects across databases raise an error" 264 # Create a book and author on the default database 265 pro = Book.objects.create(title="Pro Django", 266 published=datetime.date(2008, 12, 16)) 267 267 268 #marty = Person.objects.create(name="Marty Alchin")268 marty = Person.objects.create(name="Marty Alchin") 269 269 270 ## Create a book and author on the other database271 #dive = Book.objects.using('other').create(title="Dive into Python",272 #published=datetime.date(2009, 5, 4))270 # Create a book and author on the other database 271 dive = Book.objects.using('other').create(title="Dive into Python", 272 published=datetime.date(2009, 5, 4)) 273 273 274 #mark = Person.objects.using('other').create(name="Mark Pilgrim")275 ## Set a foreign key set with an object from a different database276 #try:277 #marty.book_set = [pro, dive]278 #self.fail("Shouldn't be able to assign across databases")279 #except ValueError:280 #pass274 mark = Person.objects.using('other').create(name="Mark Pilgrim") 275 # Set a foreign key set with an object from a different database 276 try: 277 marty.book_set = [pro, dive] 278 self.fail("Shouldn't be able to assign across databases") 279 except ValueError: 280 pass 281 281 282 ## Add to an m2m with an object from a different database283 #try:284 #marty.book_set.add(dive)285 #self.fail("Shouldn't be able to assign across databases")286 #except ValueError:287 #pass282 # Add to an m2m with an object from a different database 283 try: 284 marty.book_set.add(dive) 285 self.fail("Shouldn't be able to assign across databases") 286 except ValueError: 287 pass 288 288 289 ## Set a m2m with an object from a different database290 #try:291 #marty.book_set = [pro, dive]292 #self.fail("Shouldn't be able to assign across databases")293 #except ValueError:294 #pass289 # Set a m2m with an object from a different database 290 try: 291 marty.book_set = [pro, dive] 292 self.fail("Shouldn't be able to assign across databases") 293 except ValueError: 294 pass 295 295 296 ## Add to a reverse m2m with an object from a different database297 #try:298 #dive.authors.add(marty)299 #self.fail("Shouldn't be able to assign across databases")300 #except ValueError:301 #pass296 # Add to a reverse m2m with an object from a different database 297 try: 298 dive.authors.add(marty) 299 self.fail("Shouldn't be able to assign across databases") 300 except ValueError: 301 pass 302 302 303 ## Set a reverse m2m with an object from a different database304 #try:305 #dive.authors = [mark, marty]306 #self.fail("Shouldn't be able to assign across databases")307 #except ValueError:308 #pass303 # Set a reverse m2m with an object from a different database 304 try: 305 dive.authors = [mark, marty] 306 self.fail("Shouldn't be able to assign across databases") 307 except ValueError: 308 pass 309 309 310 310 def test_foreign_key_separation(self): 311 311 "FK fields are constrained to a single database" … … 401 401 self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), 402 402 []) 403 403 404 #def test_foreign_key_cross_database_protection(self):405 #"Operations that involve sharing FK objects across databases raise an error"406 ## Create a book and author on the default database407 #pro = Book.objects.create(title="Pro Django",408 #published=datetime.date(2008, 12, 16))404 def test_foreign_key_cross_database_protection(self): 405 "Operations that involve sharing FK objects across databases raise an error" 406 # Create a book and author on the default database 407 pro = Book.objects.create(title="Pro Django", 408 published=datetime.date(2008, 12, 16)) 409 409 410 #marty = Person.objects.create(name="Marty Alchin")410 marty = Person.objects.create(name="Marty Alchin") 411 411 412 ## Create a book and author on the other database413 #dive = Book.objects.using('other').create(title="Dive into Python",414 #published=datetime.date(2009, 5, 4))412 # Create a book and author on the other database 413 dive = Book.objects.using('other').create(title="Dive into Python", 414 published=datetime.date(2009, 5, 4)) 415 415 416 #mark = Person.objects.using('other').create(name="Mark Pilgrim")416 mark = Person.objects.using('other').create(name="Mark Pilgrim") 417 417 418 ## Set a foreign key with an object from a different database419 #try:420 #dive.editor = marty421 #self.fail("Shouldn't be able to assign across databases")422 #except ValueError:423 #pass418 # Set a foreign key with an object from a different database 419 try: 420 dive.editor = marty 421 self.fail("Shouldn't be able to assign across databases") 422 except ValueError: 423 pass 424 424 425 ## Set a foreign key set with an object from a different database426 #try:427 #marty.edited = [pro, dive]428 #self.fail("Shouldn't be able to assign across databases")429 #except ValueError:430 #pass425 # Set a foreign key set with an object from a different database 426 try: 427 marty.edited = [pro, dive] 428 self.fail("Shouldn't be able to assign across databases") 429 except ValueError: 430 pass 431 431 432 ## Add to a foreign key set with an object from a different database433 #try:434 #marty.edited.add(dive)435 #self.fail("Shouldn't be able to assign across databases")436 #except ValueError:437 #pass432 # Add to a foreign key set with an object from a different database 433 try: 434 marty.edited.add(dive) 435 self.fail("Shouldn't be able to assign across databases") 436 except ValueError: 437 pass 438 438 439 ## BUT! if you assign a FK object when the base object hasn't440 ## been saved yet, you implicitly assign the database for the441 ## base object.442 #chris = Person(name="Chris Mills")443 #html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15))444 ## initially, no db assigned445 #self.assertEquals(chris._state.db, None)446 #self.assertEquals(html5._state.db, None)439 # BUT! if you assign a FK object when the base object hasn't 440 # been saved yet, you implicitly assign the database for the 441 # base object. 442 chris = Person(name="Chris Mills") 443 html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) 444 # initially, no db assigned 445 self.assertEquals(chris._state.db, None) 446 self.assertEquals(html5._state.db, None) 447 447 448 ## old object comes from 'other', so the new object is set to use 'other'...449 #dive.editor = chris450 #html5.editor = mark451 # #self.assertEquals(chris._state.db, 'other')452 #self.assertEquals(html5._state.db, 'other')453 ## ... but it isn't saved yet454 #self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)),455 #[u'Mark Pilgrim'])456 #self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),457 #[u'Dive into Python'])448 # old object comes from 'other', so the new object is set to use 'other'... 449 dive.editor = chris 450 html5.editor = mark 451 self.assertEquals(chris._state.db, 'other') 452 self.assertEquals(html5._state.db, 'other') 453 # ... but it isn't saved yet 454 self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)), 455 [u'Mark Pilgrim']) 456 self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), 457 [u'Dive into Python']) 458 458 459 ## When saved (no using required), new objects goes to 'other'460 #chris.save()461 #html5.save()462 #self.assertEquals(list(Person.objects.using('default').values_list('name',flat=True)),463 #[u'Marty Alchin'])464 #self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)),465 #[u'Chris Mills', u'Mark Pilgrim'])466 #self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),467 #[u'Pro Django'])468 #self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),469 #[u'Dive into HTML5', u'Dive into Python'])459 # When saved (no using required), new objects goes to 'other' 460 chris.save() 461 html5.save() 462 self.assertEquals(list(Person.objects.using('default').values_list('name',flat=True)), 463 [u'Marty Alchin']) 464 self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)), 465 [u'Chris Mills', u'Mark Pilgrim']) 466 self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), 467 [u'Pro Django']) 468 self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), 469 [u'Dive into HTML5', u'Dive into Python']) 470 470 471 ## This also works if you assign the FK in the constructor472 #water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark)473 #self.assertEquals(water._state.db, 'other')474 ## ... but it isn't saved yet475 #self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),476 #[u'Pro Django'])477 #self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),478 #[u'Dive into HTML5', u'Dive into Python'])471 # This also works if you assign the FK in the constructor 472 water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark) 473 self.assertEquals(water._state.db, 'other') 474 # ... but it isn't saved yet 475 self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), 476 [u'Pro Django']) 477 self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), 478 [u'Dive into HTML5', u'Dive into Python']) 479 479 480 ## When saved, the new book goes to 'other'481 #water.save()482 #self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),483 #[u'Pro Django'])484 #self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),485 #[u'Dive into HTML5', u'Dive into Python', u'Dive into Water'])480 # When saved, the new book goes to 'other' 481 water.save() 482 self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)), 483 [u'Pro Django']) 484 self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)), 485 [u'Dive into HTML5', u'Dive into Python', u'Dive into Water']) 486 486 487 487 def test_generic_key_separation(self): 488 488 "Generic fields are constrained to a single database" … … 555 555 self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), 556 556 [u'Python Daily']) 557 557 558 #def test_generic_key_cross_database_protection(self):559 ## "Operations that involve sharing FKobjects across databases raise an error"560 ### Create a book and author on the default database561 ##pro = Book.objects.create(title="Pro Django",562 ##published=datetime.date(2008, 12, 16))558 def test_generic_key_cross_database_protection(self): 559 "Operations that involve sharing generic key objects across databases raise an error" 560 # Create a book and author on the default database 561 pro = Book.objects.create(title="Pro Django", 562 published=datetime.date(2008, 12, 16)) 563 563 564 ##review1 = Review.objects.create(source="Python Monthly", content_object=pro)564 review1 = Review.objects.create(source="Python Monthly", content_object=pro) 565 565 566 ### Create a book and author on the other database567 ##dive = Book.objects.using('other').create(title="Dive into Python",568 ##published=datetime.date(2009, 5, 4))566 # Create a book and author on the other database 567 dive = Book.objects.using('other').create(title="Dive into Python", 568 published=datetime.date(2009, 5, 4)) 569 569 570 ##review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive)570 review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive) 571 571 572 ### Set a foreign key with an object from a different database573 ##try:574 ##review1.content_object = dive575 ##self.fail("Shouldn't be able to assign across databases")576 ##except ValueError:577 ##pass572 # Set a foreign key with an object from a different database 573 try: 574 review1.content_object = dive 575 self.fail("Shouldn't be able to assign across databases") 576 except ValueError: 577 pass 578 578 579 ## Add to a foreign key set with an object from a different database580 #try:581 #dive.reviews.add(review1)582 #self.fail("Shouldn't be able to assign across databases")583 #except ValueError:584 #pass579 # Add to a foreign key set with an object from a different database 580 try: 581 dive.reviews.add(review1) 582 self.fail("Shouldn't be able to assign across databases") 583 except ValueError: 584 pass 585 585 586 ## BUT! if you assign a FK object when the base object hasn't587 ## been saved yet, you implicitly assign the database for the588 ## base object.589 #review3 = Review(source="Python Daily")590 ## initially, no db assigned591 #self.assertEquals(review3._state.db, None)586 # BUT! if you assign a FK object when the base object hasn't 587 # been saved yet, you implicitly assign the database for the 588 # base object. 589 review3 = Review(source="Python Daily") 590 # initially, no db assigned 591 self.assertEquals(review3._state.db, None) 592 592 593 ## Dive comes from 'other', so review3 is set to use 'other'...594 #review3.content_object = dive595 #self.assertEquals(review3._state.db, 'other')596 ## ... but it isn't saved yet597 #self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)),598 #[u'Python Monthly'])599 #self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)),600 #[u'Python Weekly'])593 # Dive comes from 'other', so review3 is set to use 'other'... 594 review3.content_object = dive 595 self.assertEquals(review3._state.db, 'other') 596 # ... but it isn't saved yet 597 self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)), 598 [u'Python Monthly']) 599 self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)), 600 [u'Python Weekly']) 601 601 602 ## When saved, John goes to 'other'603 #review3.save()604 #self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)),605 #[u'Python Monthly'])606 #self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)),607 #[u'Python Daily', u'Python Weekly'])602 # When saved, John goes to 'other' 603 review3.save() 604 self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)), 605 [u'Python Monthly']) 606 self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)), 607 [u'Python Daily', u'Python Weekly']) 608 608 609 609 def test_ordering(self): 610 610 "get_next_by_XXX commands stick to a single database" … … 630 630 val = Book.objects.raw('SELECT id FROM "multiple_database_book"').using('other') 631 631 self.assertEqual(map(lambda o: o.pk, val), [dive.pk]) 632 632 633 class MasterSlaveTestCase(TestCase): 634 multi_db = True 635 636 def setUp(self): 637 # Make the 'other' database appear to be a slave of the 'default' 638 self.old_source = settings.DATABASES['other']['SOURCE'] 639 settings.DATABASES['other']['SOURCE'] = 'default' 640 641 def tearDown(self): 642 # Restore the 'other' database as an independent database 643 settings.DATABASES['other']['SOURCE'] = self.old_source 644 645 def test_foreign_key_cross_database_protection(self): 646 "Foreign keys can cross databases if they two databases have a common source" 647 # Create a book and author on the default database 648 pro = Book.objects.create(title="Pro Django", 649 published=datetime.date(2008, 12, 16)) 650 651 marty = Person.objects.create(name="Marty Alchin") 652 653 # Create a book and author on the other database 654 dive = Book.objects.using('other').create(title="Dive into Python", 655 published=datetime.date(2009, 5, 4)) 656 657 mark = Person.objects.using('other').create(name="Mark Pilgrim") 658 659 # Set a foreign key with an object from a different database 660 try: 661 dive.editor = marty 662 except ValueError: 663 self.fail("Assignment across master/slave databases with a common source should be ok") 664 665 # Database assignments of original objects haven't changed... 666 self.assertEquals(marty._state.db, 'default') 667 self.assertEquals(pro._state.db, 'default') 668 self.assertEquals(dive._state.db, 'other') 669 self.assertEquals(mark._state.db, 'other') 670 671 # ... but they will when the affected object is saved. 672 dive.save() 673 self.assertEquals(dive._state.db, 'default') 674 675 # ...and the source database now has a copy of any object saved 676 try: 677 Book.objects.using('default').get(title='Dive into Python').delete() 678 except Book.DoesNotExist: 679 self.fail('Source database should have a copy of saved object') 680 681 # This isn't a real master-slave database, so restore the original from other 682 dive = Book.objects.using('other').get(title='Dive into Python') 683 self.assertEquals(dive._state.db, 'other') 684 685 # Set a foreign key set with an object from a different database 686 try: 687 marty.edited = [pro, dive] 688 except ValueError: 689 self.fail("Assignment across master/slave databases with a common source should be ok") 690 691 # Assignment implies a save, so database assignments of original objects have changed... 692 self.assertEquals(marty._state.db, 'default') 693 self.assertEquals(pro._state.db, 'default') 694 self.assertEquals(dive._state.db, 'default') 695 self.assertEquals(mark._state.db, 'other') 696 697 # ...and the source database now has a copy of any object saved 698 try: 699 Book.objects.using('default').get(title='Dive into Python').delete() 700 except Book.DoesNotExist: 701 self.fail('Source database should have a copy of saved object') 702 703 # This isn't a real master-slave database, so restore the original from other 704 dive = Book.objects.using('other').get(title='Dive into Python') 705 self.assertEquals(dive._state.db, 'other') 706 707 # Add to a foreign key set with an object from a different database 708 try: 709 marty.edited.add(dive) 710 except ValueError: 711 self.fail("Assignment across master/slave databases with a common source should be ok") 712 713 # Add implies a save, so database assignments of original objects have changed... 714 self.assertEquals(marty._state.db, 'default') 715 self.assertEquals(pro._state.db, 'default') 716 self.assertEquals(dive._state.db, 'default') 717 self.assertEquals(mark._state.db, 'other') 718 719 # ...and the source database now has a copy of any object saved 720 try: 721 Book.objects.using('default').get(title='Dive into Python').delete() 722 except Book.DoesNotExist: 723 self.fail('Source database should have a copy of saved object') 724 725 # This isn't a real master-slave database, so restore the original from other 726 dive = Book.objects.using('other').get(title='Dive into Python') 727 728 # If you assign a FK object when the base object hasn't 729 # been saved yet, you implicitly assign the database for the 730 # base object. 731 chris = Person(name="Chris Mills") 732 html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) 733 # initially, no db assigned 734 self.assertEquals(chris._state.db, None) 735 self.assertEquals(html5._state.db, None) 736 737 # old object comes from 'other', so the new object is set to use the 738 # source of 'other'... 739 self.assertEquals(dive._state.db, 'other') 740 dive.editor = chris 741 html5.editor = mark 742 743 self.assertEquals(dive._state.db, 'other') 744 self.assertEquals(mark._state.db, 'other') 745 self.assertEquals(chris._state.db, 'default') 746 self.assertEquals(html5._state.db, 'default') 747 748 # This also works if you assign the FK in the constructor 749 water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark) 750 self.assertEquals(water._state.db, 'default') 751 752 def test_m2m_cross_database_protection(self): 753 "M2M relations can cross databases if the database share a source" 754 # Create books and authors on the inverse to the usual database 755 pro = Book.objects.using('other').create(pk=1, title="Pro Django", 756 published=datetime.date(2008, 12, 16)) 757 758 marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") 759 760 dive = Book.objects.using('default').create(pk=2, title="Dive into Python", 761 published=datetime.date(2009, 5, 4)) 762 763 mark = Person.objects.using('default').create(pk=2, name="Mark Pilgrim") 764 765 # Now save back onto the usual databse. 766 # This simulates master/slave - the objects exist on both database, 767 # but the _state.db is as it is for all other tests. 768 pro.save(using='default') 769 marty.save(using='default') 770 dive.save(using='other') 771 mark.save(using='other') 772 773 # Check that we have 2 of both types of object on both databases 774 self.assertEquals(Book.objects.using('default').count(), 2) 775 self.assertEquals(Book.objects.using('other').count(), 2) 776 self.assertEquals(Person.objects.using('default').count(), 2) 777 self.assertEquals(Person.objects.using('other').count(), 2) 778 779 # Set a m2m set with an object from a different database 780 try: 781 marty.book_set = [pro, dive] 782 except ValueError: 783 self.fail("Assignment across master/slave databases with a common source should be ok") 784 785 # Database assignments don't change 786 self.assertEquals(marty._state.db, 'default') 787 self.assertEquals(pro._state.db, 'default') 788 self.assertEquals(dive._state.db, 'other') 789 self.assertEquals(mark._state.db, 'other') 790 791 # All m2m relations should be saved on the default database 792 self.assertEquals(Book.authors.through.objects.using('default').count(), 2) 793 self.assertEquals(Book.authors.through.objects.using('other').count(), 0) 794 795 # Reset relations 796 Book.authors.through.objects.using('default').delete() 797 798 # Add to an m2m with an object from a different database 799 try: 800 marty.book_set.add(dive) 801 except ValueError: 802 self.fail("Assignment across master/slave databases with a common source should be ok") 803 804 # Database assignments don't change 805 self.assertEquals(marty._state.db, 'default') 806 self.assertEquals(pro._state.db, 'default') 807 self.assertEquals(dive._state.db, 'other') 808 self.assertEquals(mark._state.db, 'other') 809 810 # All m2m relations should be saved on the default database 811 self.assertEquals(Book.authors.through.objects.using('default').count(), 1) 812 self.assertEquals(Book.authors.through.objects.using('other').count(), 0) 813 814 # Reset relations 815 Book.authors.through.objects.using('default').delete() 816 817 # Set a reverse m2m with an object from a different database 818 try: 819 dive.authors = [mark, marty] 820 except ValueError: 821 self.fail("Assignment across master/slave databases with a common source should be ok") 822 823 # Database assignments don't change 824 self.assertEquals(marty._state.db, 'default') 825 self.assertEquals(pro._state.db, 'default') 826 self.assertEquals(dive._state.db, 'other') 827 self.assertEquals(mark._state.db, 'other') 828 829 # All m2m relations should be saved on the default database 830 self.assertEquals(Book.authors.through.objects.using('default').count(), 2) 831 self.assertEquals(Book.authors.through.objects.using('other').count(), 0) 832 833 # Reset relations 834 Book.authors.through.objects.using('default').delete() 835 836 self.assertEquals(Book.authors.through.objects.using('default').count(), 0) 837 self.assertEquals(Book.authors.through.objects.using('other').count(), 0) 838 839 # Add to a reverse m2m with an object from a different database 840 try: 841 dive.authors.add(marty) 842 except ValueError: 843 self.fail("Assignment across master/slave databases with a common source should be ok") 844 845 # Database assignments don't change 846 self.assertEquals(marty._state.db, 'default') 847 self.assertEquals(pro._state.db, 'default') 848 self.assertEquals(dive._state.db, 'other') 849 self.assertEquals(mark._state.db, 'other') 850 851 # All m2m relations should be saved on the default database 852 self.assertEquals(Book.authors.through.objects.using('default').count(), 1) 853 self.assertEquals(Book.authors.through.objects.using('other').count(), 0) 854 855 def test_generic_key_cross_database_protection(self): 856 "Generic Key operations can span databases if they share a source" 857 # Create a book and author on the default database 858 pro = Book.objects.create(title="Pro Django", 859 published=datetime.date(2008, 12, 16)) 860 861 review1 = Review.objects.create(source="Python Monthly", content_object=pro) 862 863 # Create a book and author on the other database 864 dive = Book.objects.using('other').create(title="Dive into Python", 865 published=datetime.date(2009, 5, 4)) 866 867 review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive) 868 869 # Set a generic foreign key with an object from a different database 870 try: 871 review1.content_object = dive 872 except ValueError: 873 self.fail("Assignment across master/slave databases with a common source should be ok") 874 875 # Database assignments of original objects haven't changed... 876 self.assertEquals(pro._state.db, 'default') 877 self.assertEquals(review1._state.db, 'default') 878 self.assertEquals(dive._state.db, 'other') 879 self.assertEquals(review2._state.db, 'other') 880 881 # ... but they will when the affected object is saved. 882 dive.save() 883 self.assertEquals(review1._state.db, 'default') 884 self.assertEquals(dive._state.db, 'default') 885 886 # ...and the source database now has a copy of any object saved 887 try: 888 Book.objects.using('default').get(title='Dive into Python').delete() 889 except Book.DoesNotExist: 890 self.fail('Source database should have a copy of saved object') 891 892 # This isn't a real master-slave database, so restore the original from other 893 dive = Book.objects.using('other').get(title='Dive into Python') 894 self.assertEquals(dive._state.db, 'other') 895 896 # Add to a generic foreign key set with an object from a different database 897 try: 898 dive.reviews.add(review1) 899 except ValueError: 900 self.fail("Assignment across master/slave databases with a common source should be ok") 901 902 # Database assignments of original objects haven't changed... 903 self.assertEquals(pro._state.db, 'default') 904 self.assertEquals(review1._state.db, 'default') 905 self.assertEquals(dive._state.db, 'other') 906 self.assertEquals(review2._state.db, 'other') 907 908 # ... but they will when the affected object is saved. 909 dive.save() 910 self.assertEquals(dive._state.db, 'default') 911 912 # ...and the source database now has a copy of any object saved 913 try: 914 Book.objects.using('default').get(title='Dive into Python').delete() 915 except Book.DoesNotExist: 916 self.fail('Source database should have a copy of saved object') 917 918 # BUT! if you assign a FK object when the base object hasn't 919 # been saved yet, you implicitly assign the database for the 920 # base object. 921 review3 = Review(source="Python Daily") 922 # initially, no db assigned 923 self.assertEquals(review3._state.db, None) 924 925 # Dive comes from 'other', so review3 is set to use the source of 'other'... 926 review3.content_object = dive 927 self.assertEquals(review3._state.db, 'default') 928 633 929 634 930 class UserProfileTestCase(TestCase): 635 931 def setUp(self):