Making SQL management commands migration aware.

This commit is contained in:
Víðir Valberg Guðmundsson 2014-05-29 23:03:10 +02:00
parent 62f9508ade
commit cb9c9a7b58
7 changed files with 107 additions and 0 deletions

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
from optparse import make_option from optparse import make_option
from django.core.management.base import AppCommand from django.core.management.base import AppCommand
from django.core.management.sql import check_for_migrations
from django.db import connections, DEFAULT_DB_ALIAS from django.db import connections, DEFAULT_DB_ALIAS
@ -22,6 +23,7 @@ class Command(AppCommand):
if app_config.models_module is None: if app_config.models_module is None:
return return
connection = connections[options.get('database')] connection = connections[options.get('database')]
check_for_migrations(app_config, connection)
models = app_config.get_models(include_auto_created=True) models = app_config.get_models(include_auto_created=True)
statements = connection.ops.sequence_reset_sql(self.style, models) statements = connection.ops.sequence_reset_sql(self.style, models)
return '\n'.join(statements) return '\n'.join(statements)

View File

@ -9,12 +9,21 @@ from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db import models, router from django.db import models, router
from django.db.migrations.loader import MigrationLoader
from django.utils.deprecation import RemovedInDjango19Warning from django.utils.deprecation import RemovedInDjango19Warning
def check_for_migrations(app_config, connection):
loader = MigrationLoader(connection)
if app_config.label in loader.migrated_apps:
raise CommandError("App '%s' has migrations. Only the sqlmigrate and sqlflush commands can be used when an app has migrations." % app_config.label)
def sql_create(app_config, style, connection): def sql_create(app_config, style, connection):
"Returns a list of the CREATE TABLE SQL statements for the given app." "Returns a list of the CREATE TABLE SQL statements for the given app."
check_for_migrations(app_config, connection)
if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy': if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
# This must be the "dummy" database backend, which means the user # This must be the "dummy" database backend, which means the user
# hasn't set ENGINE for the database. # hasn't set ENGINE for the database.
@ -61,6 +70,8 @@ def sql_create(app_config, style, connection):
def sql_delete(app_config, style, connection): def sql_delete(app_config, style, connection):
"Returns a list of the DROP TABLE SQL statements for the given app." "Returns a list of the DROP TABLE SQL statements for the given app."
check_for_migrations(app_config, connection)
# This should work even if a connection isn't available # This should work even if a connection isn't available
try: try:
cursor = connection.cursor() cursor = connection.cursor()
@ -122,6 +133,9 @@ def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_
def sql_custom(app_config, style, connection): def sql_custom(app_config, style, connection):
"Returns a list of the custom table modifying SQL statements for the given app." "Returns a list of the custom table modifying SQL statements for the given app."
check_for_migrations(app_config, connection)
output = [] output = []
app_models = router.get_migratable_models(app_config, connection.alias) app_models = router.get_migratable_models(app_config, connection.alias)
@ -134,6 +148,9 @@ def sql_custom(app_config, style, connection):
def sql_indexes(app_config, style, connection): def sql_indexes(app_config, style, connection):
"Returns a list of the CREATE INDEX SQL statements for all models in the given app." "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
check_for_migrations(app_config, connection)
output = [] output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True): for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_indexes_for_model(model, style)) output.extend(connection.creation.sql_indexes_for_model(model, style))
@ -142,6 +159,9 @@ def sql_indexes(app_config, style, connection):
def sql_destroy_indexes(app_config, style, connection): def sql_destroy_indexes(app_config, style, connection):
"Returns a list of the DROP INDEX SQL statements for all models in the given app." "Returns a list of the DROP INDEX SQL statements for all models in the given app."
check_for_migrations(app_config, connection)
output = [] output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True): for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_destroy_indexes_for_model(model, style)) output.extend(connection.creation.sql_destroy_indexes_for_model(model, style))
@ -149,6 +169,9 @@ def sql_destroy_indexes(app_config, style, connection):
def sql_all(app_config, style, connection): def sql_all(app_config, style, connection):
check_for_migrations(app_config, connection)
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
return sql_create(app_config, style, connection) + sql_custom(app_config, style, connection) + sql_indexes(app_config, style, connection) return sql_create(app_config, style, connection) + sql_custom(app_config, style, connection) + sql_indexes(app_config, style, connection)

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('title', models.CharField(db_index=True, max_length=100)),
('comments', models.ManyToManyField(to='commands_sql_migrations.Comment')),
],
options={
},
bases=(models.Model,),
),
]

View File

@ -0,0 +1,10 @@
from django.db import models
class Comment(models.Model):
pass
class Book(models.Model):
title = models.CharField(max_length=100, db_index=True)
comments = models.ManyToManyField(Comment)

View File

@ -0,0 +1,39 @@
from __future__ import unicode_literals
from django.apps import apps
from django.core.management import CommandError
from django.core.management.color import no_style
from django.core.management.sql import (sql_create, sql_delete, sql_indexes,
sql_destroy_indexes, sql_all)
from django.db import connections, DEFAULT_DB_ALIAS, router
from django.test import TestCase
class SQLCommandsMigrationsTestCase(TestCase):
"""Tests that apps with migrations can not use sql commands."""
def test_sql_create(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_create(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
def test_sql_delete(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_delete(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
def test_sql_indexes(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
def test_sql_destroy_indexes(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_destroy_indexes(app_config, no_style(),
connections[DEFAULT_DB_ALIAS])
def test_sql_all(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_all(app_config, no_style(), connections[DEFAULT_DB_ALIAS])