fix: required columns on newly created tables retain migration safety checks

This commit is contained in:
ftrinidad committed 2026-10-28 18:22:33 -03:00
1 parent d55ba66d44
commit c7a4376d35
5 files changed
+126 -6

No files matched your search

+50 -1
View File
@@ -5,9 +5,11 @@ import unittest
from unittest.mock import patch
from django.db import ProgrammingError
from django.db import migrations
from django.db.migrations import Migration
from django_migration_linter import MigrationLinter
from django_migration_linter.sql_analyser import get_sql_analyser_class
class LinterFunctionsTestCase(unittest.TestCase):
@@ -77,7 +79,18 @@ class LinterFunctionsTestCase(unittest.TestCase):
self.assertFalse(linter.should_ignore_migration("app_correct", "0003_bar"))
def test_gather_all_migrations(self):
linter = MigrationLinter()
linter = MigrationLinter.__new__(MigrationLinter)
linter.exclude_migration_tests = []
linter.sql_analyser_class = get_sql_analyser_class("sqlite")
linter.include_apps = None
linter.exclude_apps = None
linter.ignore_name_contains = None
linter.include_name_contains = None
linter.ignore_name = []
linter.include_name = []
linter.only_applied_migrations = False
linter.only_unapplied_migrations = False
linter.ignore_initial_migrations = False
migrations = linter._gather_all_migrations()
self.assertGreater(len(list(migrations)), 1)
@@ -182,3 +195,39 @@ class LinterFunctionsTestCase(unittest.TestCase):
self.assertFalse(
linter.should_ignore_migration("app_correct", "0002_foo", is_initial=False)
)
def test_initial_migration_still_lints_runsql_and_runpython(self):
def forward(apps, schema_editor):
return None
migration = Migration("0001_initial", "app_initial_operations")
migration.initial = True
migration.operations = [
migrations.RunSQL("DROP TABLE legacy_data;"),
migrations.RunPython(forward),
]
linter = MigrationLinter.__new__(MigrationLinter)
linter.exclude_migration_tests = []
linter.sql_analyser_class = get_sql_analyser_class("sqlite")
linter.include_apps = None
linter.exclude_apps = None
linter.ignore_name_contains = None
linter.include_name_contains = None
linter.ignore_name = []
linter.include_name = []
linter.only_applied_migrations = False
linter.only_unapplied_migrations = False
linter.ignore_initial_migrations = False
errors, _, warnings = linter.analyse_data_migration(migration)
self.assertFalse(
linter.should_ignore_migration(
migration.app_label, migration.name, is_initial=migration.initial
)
)
self.assertEqual(["DROP_TABLE"], [issue.code for issue in errors])
self.assertEqual(
["RUNSQL_REVERSIBLE", "RUNPYTHON_REVERSIBLE"],
[issue.code for issue in warnings],
)
+28
View File
@@ -221,6 +221,34 @@ class SqliteAnalyserTestCase(SqlAnalyserTestCase):
class PostgresqlAnalyserTestCase(SqlAnalyserTestCase):
database_vendor = "postgresql"
def test_add_required_foreign_key_to_new_table(self):
sql = [
'CREATE TABLE "archive_capture" ("id" uuid NOT NULL PRIMARY KEY);',
'ALTER TABLE "archive_capture" ADD COLUMN "source_id" uuid NOT NULL REFERENCES "archive_source" ("id") DEFERRABLE INITIALLY DEFERRED;',
]
self.assertValidSql(sql)
def test_add_required_foreign_key_to_existing_table(self):
sql = [
'CREATE TABLE "archive_capture" ("id" uuid NOT NULL PRIMARY KEY);',
'ALTER TABLE "other_app_capture" ADD COLUMN "source_id" uuid NOT NULL REFERENCES "archive_source" ("id") DEFERRABLE INITIALLY DEFERRED;',
]
self.assertBackwardIncompatibleSql(sql, "NOT_NULL")
def test_add_required_foreign_key_after_conditional_create(self):
sql = [
'CREATE TABLE IF NOT EXISTS "legacy" ("id" uuid NOT NULL PRIMARY KEY);',
'ALTER TABLE "legacy" ADD COLUMN "source_id" uuid NOT NULL REFERENCES "archive_source" ("id") DEFERRABLE INITIALLY DEFERRED;',
]
self.assertBackwardIncompatibleSql(sql, "NOT_NULL")
def test_add_required_foreign_key_to_new_qualified_table(self):
sql = [
'CREATE TABLE "archive"."capture" ("id" uuid NOT NULL PRIMARY KEY);',
'ALTER TABLE "archive"."capture" ADD COLUMN "source_id" uuid NOT NULL REFERENCES "archive"."source" ("id") DEFERRABLE INITIALLY DEFERRED;',
]
self.assertValidSql(sql)
def test_alter_column(self):
sql = 'ALTER TABLE "app_alter_column_a" ALTER COLUMN "field" TYPE varchar(10) USING "field"::varchar(10);'
self.assertBackwardIncompatibleSql(sql)