Ticket #16039: 16039.patch
File 16039.patch, 13.4 KB (added by , 12 years ago) |
---|
-
django/contrib/auth/management/__init__.py
commit 587072824e819884989dc659068dde0e59452514 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Thu Nov 22 20:09:40 2012 +0100 Fixed #16039 -- Made post_syncdb handlers multi-db aware. Also reverted 8fb7a9002669fb7ba7bec7df90b465b92e1ed3c2. Refs #17055. diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index b5fd29a..ce5d57f 100644
a b import unicodedata 10 10 from django.contrib.auth import models as auth_app, get_user_model 11 11 from django.core import exceptions 12 12 from django.core.management.base import CommandError 13 from django.db import DEFAULT_DB_ALIAS, router 13 14 from django.db.models import get_models, signals 14 15 from django.utils import six 15 16 from django.utils.six.moves import input … … def _check_permission_clashing(custom, builtin, ctype): 57 58 (codename, ctype.app_label, ctype.model_class().__name__)) 58 59 pool.add(codename) 59 60 60 def create_permissions(app, created_models, verbosity, **kwargs): 61 def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs): 62 if not router.allow_syncdb(db, auth_app.Permission): 63 return 64 61 65 from django.contrib.contenttypes.models import ContentType 62 66 63 67 app_models = get_models(app) … … def create_permissions(app, created_models, verbosity, **kwargs): 68 72 # The codenames and ctypes that should exist. 69 73 ctypes = set() 70 74 for klass in app_models: 71 ctype = ContentType.objects.get_for_model(klass) 75 # Force looking up the content types in the current database 76 # before creating foreign keys to them. 77 ctype = ContentType.objects.db_manager(db).get_for_model(klass) 72 78 ctypes.add(ctype) 73 79 for perm in _get_all_permissions(klass._meta, ctype): 74 80 searched_perms.append((ctype, perm)) … … def create_permissions(app, created_models, verbosity, **kwargs): 76 82 # Find all the Permissions that have a context_type for a model we're 77 83 # looking for. We don't need to check for codenames since we already have 78 84 # a list of the ones we're going to create. 79 all_perms = set(auth_app.Permission.objects. filter(85 all_perms = set(auth_app.Permission.objects.using(db).filter( 80 86 content_type__in=ctypes, 81 87 ).values_list( 82 88 "content_type", "codename" 83 89 )) 84 90 85 objs = [91 perms = [ 86 92 auth_app.Permission(codename=codename, name=name, content_type=ctype) 87 93 for ctype, (codename, name) in searched_perms 88 94 if (ctype.pk, codename) not in all_perms 89 95 ] 90 auth_app.Permission.objects. bulk_create(objs)96 auth_app.Permission.objects.using(db).bulk_create(perms) 91 97 if verbosity >= 2: 92 for obj in objs:93 print("Adding permission '%s'" % obj)98 for perm in perms: 99 print("Adding permission '%s'" % perm) 94 100 95 101 96 102 def create_superuser(app, created_models, verbosity, db, **kwargs): -
django/contrib/contenttypes/management.py
diff --git a/django/contrib/contenttypes/management.py b/django/contrib/contenttypes/management.py index 9f287d4..85f76ba 100644
a b 1 1 from django.contrib.contenttypes.models import ContentType 2 from django.db import DEFAULT_DB_ALIAS, router 2 3 from django.db.models import get_apps, get_models, signals 3 4 from django.utils.encoding import smart_text 4 5 from django.utils import six 5 6 from django.utils.six.moves import input 6 7 7 def update_contenttypes(app, created_models, verbosity=2, **kwargs):8 def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, **kwargs): 8 9 """ 9 10 Creates content types for models in the given app, removing any model 10 11 entries that no longer have a matching model class. 11 12 """ 13 if not router.allow_syncdb(db, ContentType): 14 return 15 12 16 ContentType.objects.clear_cache() 13 17 app_models = get_models(app) 14 18 if not app_models: … … def update_contenttypes(app, created_models, verbosity=2, **kwargs): 19 23 (model._meta.object_name.lower(), model) 20 24 for model in app_models 21 25 ) 26 22 27 # Get all the content types 23 28 content_types = dict( 24 29 (ct.model, ct) 25 for ct in ContentType.objects. filter(app_label=app_label)30 for ct in ContentType.objects.using(db).filter(app_label=app_label) 26 31 ) 27 32 to_remove = [ 28 33 ct … … def update_contenttypes(app, created_models, verbosity=2, **kwargs): 30 35 if model_name not in app_models 31 36 ] 32 37 33 cts = ContentType.objects.bulk_create([38 cts = [ 34 39 ContentType( 35 40 name=smart_text(model._meta.verbose_name_raw), 36 41 app_label=app_label, … … def update_contenttypes(app, created_models, verbosity=2, **kwargs): 38 43 ) 39 44 for (model_name, model) in six.iteritems(app_models) 40 45 if model_name not in content_types 41 ]) 46 ] 47 ContentType.objects.using(db).bulk_create(cts) 42 48 if verbosity >= 2: 43 49 for ct in cts: 44 50 print("Adding content type '%s | %s'" % (ct.app_label, ct.model)) -
docs/releases/1.5.txt
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index b73bb04..e26b927 100644
a b with the :meth:`~django.forms.Form.is_valid()` method and not with the 551 551 presence or absence of the :attr:`~django.forms.Form.cleaned_data` attribute 552 552 on the form. 553 553 554 Behavior of :djadmin:`syncdb` with multiple databases 555 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 556 557 :djadmin:`syncdb` now queries the database routers to determine if content 558 types (when :mod:`~django.contrib.contenttypes` is enabled) and permissions 559 (when :mod:`~django.contrib.auth` is enabled) should be created in the target 560 database. Previously, it created them in the default database, even when 561 another database was specified with the :djadminopt:`--database` option. 562 563 If you use :djadmin:`syncdb` on multiple databases, you should ensure that 564 your routers allow synchronizing content types and permissions to only one of 565 them. See the docs on the :ref:`behavior of contrib apps with multiple 566 databases <contrib_app_multiple_databases>` for more information. 567 554 568 Miscellaneous 555 569 ~~~~~~~~~~~~~ 556 570 -
docs/topics/db/multi-db.txt
diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index d2ff864..9c0fa23 100644
a b However, if you're using SQLite or MySQL with MyISAM tables, there is 630 630 no enforced referential integrity; as a result, you may be able to 631 631 'fake' cross database foreign keys. However, this configuration is not 632 632 officially supported by Django. 633 634 .. _contrib_app_multiple_databases: 635 636 Behavior of contrib apps 637 ------------------------ 638 639 Several contrib apps include models, and some apps depend on others. Since 640 cross-database relationships are impossible, this creates some restrictions on 641 how you can split these models across databases: 642 643 - each one of ``contenttypes.ContentType``, ``sessions.Session`` and 644 ``sites.Site`` can be stored in any database, given a suitable router. 645 - ``auth`` models — ``User``, ``Group`` and ``Permission`` — are linked 646 together and linked to ``ContentType``, so they must be stored in the same 647 database as ``ContentType``. 648 - ``admin`` and ``comments`` depend on ``auth``, so their models must be in 649 the same database as ``auth``. 650 - ``flatpages`` and ``redirects`` depend on ``sites``, so their models must be 651 in the same database as ``sites``. 652 653 In addition, some objects are automatically created just after 654 :djadmin:`syncdb` creates a table to hold them in a database: 655 656 - a default ``Site``, 657 - a ``ContentType`` for each model (including those not stored to that 658 database), 659 - three ``Permission`` for each model (same comment). 660 661 .. versionchanged:: 1.5 662 Previously, ``ContentType`` and ``Permission`` instances were created only 663 in the default database. 664 665 For common setups with multiple databases, it isn't useful to have these 666 objects in more than one database. Common setups include master / slave and 667 connecting to external databases. Therefore, it's recommended: 668 669 - either to run :djadmin:`syncdb` only for the default database; 670 - or to write :ref:`database router<topics-db-multi-db-routing>` that allows 671 synchronizing these three models only to one database. 672 673 .. warning:: 674 675 If you're synchronizing content types to more that one database, be aware 676 that their primary keys may not match across databases. This may result in 677 data corruption or data loss. -
tests/regressiontests/multiple_database/tests.py
diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 79fe6be..a1d9e93 100644
a b from django.utils.six import StringIO 16 16 from .models import Book, Person, Pet, Review, UserProfile 17 17 18 18 19 def copy_content_types_from_default_to_other():20 # On post_syncdb, content types are created in the 'default' database.21 # However, tests of generic foreign keys require them in 'other' too.22 # The problem is masked on backends that defer constraints checks: at the23 # end of each test, there's a rollback, and constraints are never checked.24 # It only appears on MySQL + InnoDB.25 for ct in ContentType.objects.using('default').all():26 ct.save(using='other')27 28 29 19 class QueryTestCase(TestCase): 30 20 multi_db = True 31 21 … … class QueryTestCase(TestCase): 705 695 706 696 def test_generic_key_separation(self): 707 697 "Generic fields are constrained to a single database" 708 copy_content_types_from_default_to_other()709 710 698 # Create a book and author on the default database 711 699 pro = Book.objects.create(title="Pro Django", 712 700 published=datetime.date(2008, 12, 16)) … … class QueryTestCase(TestCase): 734 722 735 723 def test_generic_key_reverse_operations(self): 736 724 "Generic reverse manipulations are all constrained to a single DB" 737 copy_content_types_from_default_to_other()738 739 725 dive = Book.objects.using('other').create(title="Dive into Python", 740 726 published=datetime.date(2009, 5, 4)) 741 727 … … class QueryTestCase(TestCase): 780 766 781 767 def test_generic_key_cross_database_protection(self): 782 768 "Operations that involve sharing generic key objects across databases raise an error" 783 copy_content_types_from_default_to_other()784 785 769 # Create a book and author on the default database 786 770 pro = Book.objects.create(title="Pro Django", 787 771 published=datetime.date(2008, 12, 16)) … … class QueryTestCase(TestCase): 833 817 834 818 def test_generic_key_deletion(self): 835 819 "Cascaded deletions of Generic Key relations issue queries on the right database" 836 copy_content_types_from_default_to_other()837 838 820 dive = Book.objects.using('other').create(title="Dive into Python", 839 821 published=datetime.date(2009, 5, 4)) 840 822 review = Review.objects.using('other').create(source="Python Weekly", content_object=dive) … … class RouterTestCase(TestCase): 1402 1384 1403 1385 def test_generic_key_cross_database_protection(self): 1404 1386 "Generic Key operations can span databases if they share a source" 1405 copy_content_types_from_default_to_other()1406 1407 1387 # Create a book and author on the default database 1408 1388 pro = Book.objects.using('default' 1409 1389 ).create(title="Pro Django", published=datetime.date(2008, 12, 16)) … … class RouterTestCase(TestCase): 1515 1495 1516 1496 def test_generic_key_managers(self): 1517 1497 "Generic key relations are represented by managers, and can be controlled like managers" 1518 copy_content_types_from_default_to_other()1519 1520 1498 pro = Book.objects.using('other').create(title="Pro Django", 1521 1499 published=datetime.date(2008, 12, 16)) 1522 1500 … … class RouterModelArgumentTestCase(TestCase): 1922 1900 pet = Pet.objects.create(owner=person, name='Wart') 1923 1901 # test related FK collection 1924 1902 person.delete() 1903 1904 1905 class SyncOnlyDefaultDatabaseRouter(object): 1906 def allow_syncdb(self, db, model): 1907 return db == DEFAULT_DB_ALIAS 1908 1909 1910 class SyncDBTestCase(TestCase): 1911 multi_db = True 1912 1913 def test_syncdb_to_other_database(self): 1914 """Regression test for #16039: syncdb with --database option.""" 1915 count = ContentType.objects.count() 1916 self.assertGreater(count, 0) 1917 1918 ContentType.objects.using('other').delete() 1919 management.call_command('syncdb', verbosity=0, interactive=False, 1920 load_initial_data=False, database='other') 1921 1922 self.assertEqual(ContentType.objects.using("other").count(), count) 1923 1924 def test_syncdb_to_other_database_with_router(self): 1925 """Regression test for #16039: syncdb with --database option.""" 1926 ContentType.objects.using('other').delete() 1927 try: 1928 old_routers = router.routers 1929 router.routers = [SyncOnlyDefaultDatabaseRouter()] 1930 management.call_command('syncdb', verbosity=0, interactive=False, 1931 load_initial_data=False, database='other') 1932 finally: 1933 router.routers = old_routers 1934 1935 self.assertEqual(ContentType.objects.using("other").count(), 0)