summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2015-05-09 17:01:40 +0200
committerClaude Paroz <claude@2xlibre.net>2015-05-13 18:30:36 +0200
commitf61c4f490dc4c8ec6ba94ad4f40247db2425fc3e (patch)
tree8ce61422333b0b4fb850a13b1f776f0024bd0d1d
parent3c8fe5dddf34533a419d2deed5208a28de32cb4a (diff)
Fixed #24742 -- Made runserver.check_migrations ignore read-only databases
Thanks Luis Del Giudice for the report, and Aymeric Augustin and Markus Holtermann for the reviews.
-rw-r--r--django/core/management/commands/runserver.py18
-rw-r--r--django/db/migrations/exceptions.py5
-rw-r--r--django/db/migrations/recorder.py10
-rw-r--r--tests/admin_scripts/tests.py26
4 files changed, 51 insertions, 8 deletions
diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py
index e2d447147b..700da7f31d 100644
--- a/django/core/management/commands/runserver.py
+++ b/django/core/management/commands/runserver.py
@@ -12,6 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError
from django.core.servers.basehttp import get_internal_wsgi_application, run
from django.db import DEFAULT_DB_ALIAS, connections
+from django.db.migrations.exceptions import MigrationSchemaMissing
from django.db.migrations.executor import MigrationExecutor
from django.utils import autoreload, six
from django.utils.encoding import force_text, get_system_encoding
@@ -109,10 +110,7 @@ class Command(BaseCommand):
self.stdout.write("Performing system checks...\n\n")
self.check(display_num_errors=True)
- try:
- self.check_migrations()
- except ImproperlyConfigured:
- pass
+ self.check_migrations()
now = datetime.now().strftime('%B %d, %Y - %X')
if six.PY2:
now = now.decode(get_system_encoding())
@@ -157,7 +155,17 @@ class Command(BaseCommand):
Checks to see if the set of migrations on disk matches the
migrations in the database. Prints a warning if they don't match.
"""
- executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
+ try:
+ executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
+ except ImproperlyConfigured:
+ # No databases are configured (or the dummy one)
+ return
+ except MigrationSchemaMissing:
+ self.stdout.write(self.style.NOTICE(
+ "\nNot checking migrations as it is not possible to access/create the django_migrations table."
+ ))
+ return
+
plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
if plan:
self.stdout.write(self.style.NOTICE(
diff --git a/django/db/migrations/exceptions.py b/django/db/migrations/exceptions.py
index d8b386e1ac..8b60a035fe 100644
--- a/django/db/migrations/exceptions.py
+++ b/django/db/migrations/exceptions.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+from django.db.utils import DatabaseError
from django.utils.encoding import python_2_unicode_compatible
@@ -53,3 +54,7 @@ class NodeNotFoundError(LookupError):
def __repr__(self):
return "NodeNotFoundError(%r)" % self.node
+
+
+class MigrationSchemaMissing(DatabaseError):
+ pass
diff --git a/django/db/migrations/recorder.py b/django/db/migrations/recorder.py
index d1071cecb7..2dcbe6c042 100644
--- a/django/db/migrations/recorder.py
+++ b/django/db/migrations/recorder.py
@@ -2,9 +2,12 @@ from __future__ import unicode_literals
from django.apps.registry import Apps
from django.db import models
+from django.db.utils import DatabaseError
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
+from .exceptions import MigrationSchemaMissing
+
class MigrationRecorder(object):
"""
@@ -49,8 +52,11 @@ class MigrationRecorder(object):
if self.Migration._meta.db_table in self.connection.introspection.table_names(self.connection.cursor()):
return
# Make the table
- with self.connection.schema_editor() as editor:
- editor.create_model(self.Migration)
+ try:
+ with self.connection.schema_editor() as editor:
+ editor.create_model(self.Migration)
+ except DatabaseError as exc:
+ raise MigrationSchemaMissing("Unable to create the django_migrations table (%s)" % exc)
def applied_migrations(self):
"""
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py
index fbf28ad4f2..6343d89c3e 100644
--- a/tests/admin_scripts/tests.py
+++ b/tests/admin_scripts/tests.py
@@ -22,6 +22,9 @@ from django.conf import settings
from django.core.management import (
BaseCommand, CommandError, call_command, color,
)
+from django.db import ConnectionHandler
+from django.db.migrations.exceptions import MigrationSchemaMissing
+from django.db.migrations.recorder import MigrationRecorder
from django.test import LiveServerTestCase, TestCase, mock, override_settings
from django.test.runner import DiscoverRunner
from django.utils._os import npath, upath
@@ -1247,7 +1250,8 @@ class ManageRunserver(AdminScriptTestCase):
def monkey_run(*args, **options):
return
- self.cmd = Command()
+ self.output = StringIO()
+ self.cmd = Command(stdout=self.output)
self.cmd.run = monkey_run
def assertServerSettings(self, addr, port, ipv6=None, raw_ipv6=False):
@@ -1298,6 +1302,26 @@ class ManageRunserver(AdminScriptTestCase):
self.cmd.handle(addrport="deadbeef:7654")
self.assertServerSettings('deadbeef', '7654')
+ def test_no_database(self):
+ """
+ Ensure runserver.check_migrations doesn't choke on empty DATABASES.
+ """
+ tested_connections = ConnectionHandler({})
+ with mock.patch('django.core.management.commands.runserver.connections', new=tested_connections):
+ self.cmd.check_migrations()
+
+ def test_readonly_database(self):
+ """
+ Ensure runserver.check_migrations doesn't choke when a database is read-only
+ (with possibly no django_migrations table).
+ """
+ with mock.patch.object(
+ MigrationRecorder, 'ensure_schema',
+ side_effect=MigrationSchemaMissing()):
+ self.cmd.check_migrations()
+ # Check a warning is emitted
+ self.assertIn("Not checking migrations", self.output.getvalue())
+
class ManageRunserverEmptyAllowedHosts(AdminScriptTestCase):
def setUp(self):