From 76ab8851181675a59425f9637bdbf3f2de95f6f1 Mon Sep 17 00:00:00 2001 From: Jim Nicholls Date: Sat, 13 Aug 2016 19:33:58 +1000 Subject: [PATCH] Fixed #27054 -- Fixed makemigrations crash with a read-only database. --- django/db/migrations/loader.py | 8 +++++++- docs/releases/1.10.1.txt | 2 ++ tests/migrations/test_commands.py | 25 ++++++++++++++++++++++++- tests/migrations/test_loader.py | 14 ++++++++++++-- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index d459f2c5246..e5a7f031f9d 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -6,6 +6,7 @@ from importlib import import_module from django.apps import apps from django.conf import settings +from django.db.migrations.exceptions import MigrationSchemaMissing from django.db.migrations.graph import MigrationGraph from django.db.migrations.recorder import MigrationRecorder from django.utils import six @@ -273,7 +274,12 @@ class MigrationLoader(object): unapplied dependencies. """ recorder = MigrationRecorder(connection) - applied = recorder.applied_migrations() + try: + applied = recorder.applied_migrations() + except MigrationSchemaMissing: + # Skip check if the django_migrations table is missing and can't be + # created. + return for migration in applied: # If the migration is unknown, skip it. if migration not in self.graph.nodes: diff --git a/docs/releases/1.10.1.txt b/docs/releases/1.10.1.txt index a6c019c4b37..a13ecbb10ac 100644 --- a/docs/releases/1.10.1.txt +++ b/docs/releases/1.10.1.txt @@ -54,3 +54,5 @@ Bugfixes PostGIS (:ticket:`27014`). * Reallowed the ``{% for %}`` tag to unpack any iterable (:ticket:`27058`). + +* Fixed ``makemigrations`` crash if a database is read-only (:ticket:`27054`). diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 09abd21103b..dca1a467801 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -12,7 +12,9 @@ from django.core.management import CommandError, call_command from django.db import ( ConnectionHandler, DatabaseError, connection, connections, models, ) -from django.db.migrations.exceptions import InconsistentMigrationHistory +from django.db.migrations.exceptions import ( + InconsistentMigrationHistory, MigrationSchemaMissing, +) from django.db.migrations.recorder import MigrationRecorder from django.test import ignore_warnings, mock, override_settings from django.utils import six @@ -595,6 +597,27 @@ class MakeMigrationsTests(MigrationTestBase): init_file = os.path.join(migration_dir, '__init__.py') self.assertTrue(os.path.exists(init_file)) + def test_makemigrations_other_datbase_is_readonly(self): + """ + makemigrations ignores the non-default database if it's read-only. + """ + def patched_ensure_schema(migration_recorder): + from django.db import connections + if migration_recorder.connection is connections['other']: + raise MigrationSchemaMissing() + else: + return mock.DEFAULT + + self.assertTableNotExists('migrations_unicodemodel') + apps.register_model('migrations', UnicodeModel) + with mock.patch.object( + MigrationRecorder, 'ensure_schema', + autospec=True, side_effect=patched_ensure_schema): + with self.temporary_migration_module() as migration_dir: + call_command("makemigrations", "migrations", verbosity=0) + initial_file = os.path.join(migration_dir, "0001_initial.py") + self.assertTrue(os.path.exists(initial_file)) + def test_failing_migration(self): # If a migration fails to serialize, it shouldn't generate an empty file. #21280 apps.register_model('migrations', UnserializableModel) diff --git a/tests/migrations/test_loader.py b/tests/migrations/test_loader.py index c97cddb9139..b46f35f7ed2 100644 --- a/tests/migrations/test_loader.py +++ b/tests/migrations/test_loader.py @@ -4,11 +4,12 @@ from unittest import skipIf from django.db import connection, connections from django.db.migrations.exceptions import ( - AmbiguityError, InconsistentMigrationHistory, NodeNotFoundError, + AmbiguityError, InconsistentMigrationHistory, MigrationSchemaMissing, + NodeNotFoundError, ) from django.db.migrations.loader import MigrationLoader from django.db.migrations.recorder import MigrationRecorder -from django.test import TestCase, modify_settings, override_settings +from django.test import TestCase, mock, modify_settings, override_settings from django.utils import six @@ -464,3 +465,12 @@ class LoaderTests(TestCase): ('app1', '4_auto'), } self.assertEqual(plan, expected_plan) + + def test_readonly_database(self): + """ + check_consistent_history() ignores read-only databases, possibly + without a django_migrations table. + """ + with mock.patch.object(MigrationRecorder, 'ensure_schema', side_effect=MigrationSchemaMissing()): + loader = MigrationLoader(connection=None) + loader.check_consistent_history(connection)