Run some checks on RunSQL migration operation

This commit is contained in:
David-Wobrock authored and David Wobrock committed 2020-11-26 19:02:57 +01:00
1 parent 780630131f
commit f5d0da4932
3 files changed
+89 -8

No files matched your search

+1
View File
@@ -3,6 +3,7 @@
* Add Python 3.9 support
* Make data migration model import error less strict (issue #121)
* Add warning detection on RunPython call when model variable name is not the same as model class name
* Run checks on RunSQL migration operations
Switched from Travis CI to GitHub Actions for tests
+87 -8
View File
@@ -9,8 +9,8 @@ from subprocess import Popen, PIPE
from django.conf import settings
from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, connections, ProgrammingError
from django.db.migrations import RunPython
from django.db import DEFAULT_DB_ALIAS, connections, ProgrammingError, connection
from django.db.migrations import RunPython, RunSQL
from enum import Enum, unique
from six import PY2
@@ -392,12 +392,17 @@ class MigrationLinter(object):
for operation in migration.operations:
if isinstance(operation, RunPython):
op_errors, op_ignored, op_warnings = self.lint_runpython(operation)
if op_errors:
errors += op_errors
if op_ignored:
ignored += op_ignored
if op_warnings:
warnings += op_warnings
elif isinstance(operation, RunSQL):
op_errors, op_ignored, op_warnings = self.lint_runsql(operation)
else:
op_errors, op_ignored, op_warnings = [], [], []
if op_errors:
errors += op_errors
if op_ignored:
ignored += op_ignored
if op_warnings:
warnings += op_warnings
return errors, ignored, warnings
@@ -546,3 +551,77 @@ class MigrationLinter(object):
}
)
return issues
def lint_runsql(self, runsql):
error = []
ignored = []
warning = []
# Detect warning on missing reverse operation
if not runsql.reversible:
issue = {
"code": "REVERSIBLE_RUNSQL_DATA_MIGRATION",
"msg": "RunSQL data migration is not reversible",
}
if issue["code"] in self.exclude_migration_tests:
ignored.append(issue)
else:
warning.append(issue)
# Put the SQL in our SQL analyser
if runsql.sql != RunSQL.noop:
sql_statements = []
if isinstance(runsql.sql, (list, tuple)):
for sql in runsql.sql:
params = None
if isinstance(sql, (list, tuple)):
elements = len(sql)
if elements == 2:
sql, params = sql
else:
raise ValueError("Expected a 2-tuple but got %d" % elements)
sql_statements.append(sql % params)
else:
sql_statements.append(sql)
else:
sql_statements.append(runsql.sql)
sql_errors, sql_ignored = analyse_sql_statements(
sql_statements,
settings.DATABASES[self.database]["ENGINE"],
self.exclude_migration_tests,
)
if sql_errors:
error += sql_errors
if sql_ignored:
ignored += sql_ignored
# And analysse the reverse SQL
if runsql.reversible and runsql.reverse_sql != RunSQL.noop:
sql_statements = []
if isinstance(runsql.reverse_sql, (list, tuple)):
for sql in runsql.reverse_sql:
params = None
if isinstance(sql, (list, tuple)):
elements = len(sql)
if elements == 2:
sql, params = sql
else:
raise ValueError("Expected a 2-tuple but got %d" % elements)
sql_statements.append(sql % params)
else:
sql_statements.append(sql)
else:
sql_statements.append(runsql.reverse_sql)
sql_errors, sql_ignored = analyse_sql_statements(
sql_statements,
settings.DATABASES[self.database]["ENGINE"],
self.exclude_migration_tests,
)
if sql_errors:
error += sql_errors
if sql_ignored:
ignored += sql_ignored
return error, ignored, warning
+1
View File
@@ -38,3 +38,4 @@ You can ignore check through the `--exclude-migration-test` option and specifyin
|`NAMING_CONVENTION_RUNPYTHON_ARGS` | By convention, RunPython names two arguments: apps, schema_editor
|`DATA_MIGRATION_MODEL_IMPORT` | Missing apps.get_model() calls for model
|`DATA_MIGRATION_MODEL_VARIABLE_NAME` | The model variable name is different from the model class itself
|`REVERSIBLE_RUNSQL_DATA_MIGRATION` | RunSQL data migration is not reversible (missing reverse SQL)