mirror of
https://github.com/django/django.git
synced 2025-05-01 20:54:38 +00:00
Fixed #34760 -- Dropped support for SQLite < 3.27.
This commit is contained in:
parent
f46a6b2816
commit
2b582387d5
@ -134,9 +134,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor):
|
|||||||
else:
|
else:
|
||||||
super().remove_field(model, field)
|
super().remove_field(model, field)
|
||||||
|
|
||||||
def alter_db_table(
|
def alter_db_table(self, model, old_db_table, new_db_table):
|
||||||
self, model, old_db_table, new_db_table, disable_constraints=True
|
|
||||||
):
|
|
||||||
from django.contrib.gis.db.models import GeometryField
|
from django.contrib.gis.db.models import GeometryField
|
||||||
|
|
||||||
if old_db_table == new_db_table or (
|
if old_db_table == new_db_table or (
|
||||||
@ -155,7 +153,7 @@ class SpatialiteSchemaEditor(DatabaseSchemaEditor):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
# Alter table
|
# Alter table
|
||||||
super().alter_db_table(model, old_db_table, new_db_table, disable_constraints)
|
super().alter_db_table(model, old_db_table, new_db_table)
|
||||||
# Repoint any straggler names
|
# Repoint any straggler names
|
||||||
for geom_table in self.geometry_tables:
|
for geom_table in self.geometry_tables:
|
||||||
try:
|
try:
|
||||||
|
@ -174,9 +174,6 @@ class BaseDatabaseFeatures:
|
|||||||
|
|
||||||
schema_editor_uses_clientside_param_binding = False
|
schema_editor_uses_clientside_param_binding = False
|
||||||
|
|
||||||
# Does it support operations requiring references rename in a transaction?
|
|
||||||
supports_atomic_references_rename = True
|
|
||||||
|
|
||||||
# Can we issue more than one ALTER COLUMN clause in an ALTER TABLE?
|
# Can we issue more than one ALTER COLUMN clause in an ALTER TABLE?
|
||||||
supports_combined_alters = False
|
supports_combined_alters = False
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||||||
|
|
||||||
conn.execute("PRAGMA foreign_keys = ON")
|
conn.execute("PRAGMA foreign_keys = ON")
|
||||||
# The macOS bundled SQLite defaults legacy_alter_table ON, which
|
# The macOS bundled SQLite defaults legacy_alter_table ON, which
|
||||||
# prevents atomic table renames (feature supports_atomic_references_rename)
|
# prevents atomic table renames.
|
||||||
conn.execute("PRAGMA legacy_alter_table = OFF")
|
conn.execute("PRAGMA legacy_alter_table = OFF")
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from .base import Database
|
|||||||
|
|
||||||
|
|
||||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
minimum_database_version = (3, 21)
|
minimum_database_version = (3, 27)
|
||||||
test_db_allows_multiple_connections = False
|
test_db_allows_multiple_connections = False
|
||||||
supports_unspecified_pk = True
|
supports_unspecified_pk = True
|
||||||
supports_timezones = False
|
supports_timezones = False
|
||||||
@ -26,13 +26,11 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
time_cast_precision = 3
|
time_cast_precision = 3
|
||||||
can_release_savepoints = True
|
can_release_savepoints = True
|
||||||
has_case_insensitive_like = True
|
has_case_insensitive_like = True
|
||||||
# Is "ALTER TABLE ... RENAME COLUMN" supported?
|
|
||||||
can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
|
|
||||||
# Is "ALTER TABLE ... DROP COLUMN" supported?
|
# Is "ALTER TABLE ... DROP COLUMN" supported?
|
||||||
can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
|
can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
|
||||||
supports_parentheses_in_compound = False
|
supports_parentheses_in_compound = False
|
||||||
can_defer_constraint_checks = True
|
can_defer_constraint_checks = True
|
||||||
supports_over_clause = Database.sqlite_version_info >= (3, 25, 0)
|
supports_over_clause = True
|
||||||
supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
|
supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
|
||||||
supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
|
supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
|
||||||
supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
|
supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
|
||||||
@ -40,8 +38,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
|
requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
|
||||||
order_by_nulls_first = True
|
order_by_nulls_first = True
|
||||||
supports_json_field_contains = False
|
supports_json_field_contains = False
|
||||||
supports_update_conflicts = Database.sqlite_version_info >= (3, 24, 0)
|
supports_update_conflicts = True
|
||||||
supports_update_conflicts_with_target = supports_update_conflicts
|
supports_update_conflicts_with_target = True
|
||||||
test_collations = {
|
test_collations = {
|
||||||
"ci": "nocase",
|
"ci": "nocase",
|
||||||
"cs": "binary",
|
"cs": "binary",
|
||||||
@ -88,15 +86,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
"test_integer_with_negative_precision",
|
"test_integer_with_negative_precision",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if Database.sqlite_version_info < (3, 27):
|
|
||||||
skips.update(
|
|
||||||
{
|
|
||||||
"Nondeterministic failure on SQLite < 3.27.": {
|
|
||||||
"expressions_window.tests.WindowFunctionTests."
|
|
||||||
"test_subquery_row_range_rank",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if self.connection.is_in_memory_db():
|
if self.connection.is_in_memory_db():
|
||||||
skips.update(
|
skips.update(
|
||||||
{
|
{
|
||||||
@ -131,10 +120,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
)
|
)
|
||||||
return skips
|
return skips
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def supports_atomic_references_rename(self):
|
|
||||||
return Database.sqlite_version_info >= (3, 26, 0)
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def introspected_field_types(self):
|
def introspected_field_types(self):
|
||||||
return {
|
return {
|
||||||
|
@ -7,7 +7,6 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|||||||
from django.db.backends.ddl_references import Statement
|
from django.db.backends.ddl_references import Statement
|
||||||
from django.db.backends.utils import strip_quotes
|
from django.db.backends.utils import strip_quotes
|
||||||
from django.db.models import NOT_PROVIDED, UniqueConstraint
|
from django.db.models import NOT_PROVIDED, UniqueConstraint
|
||||||
from django.db.transaction import atomic
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
@ -73,105 +72,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|||||||
def prepare_default(self, value):
|
def prepare_default(self, value):
|
||||||
return self.quote_value(value)
|
return self.quote_value(value)
|
||||||
|
|
||||||
def _is_referenced_by_fk_constraint(
|
|
||||||
self, table_name, column_name=None, ignore_self=False
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Return whether or not the provided table name is referenced by another
|
|
||||||
one. If `column_name` is specified, only references pointing to that
|
|
||||||
column are considered. If `ignore_self` is True, self-referential
|
|
||||||
constraints are ignored.
|
|
||||||
"""
|
|
||||||
with self.connection.cursor() as cursor:
|
|
||||||
for other_table in self.connection.introspection.get_table_list(cursor):
|
|
||||||
if ignore_self and other_table.name == table_name:
|
|
||||||
continue
|
|
||||||
relations = self.connection.introspection.get_relations(
|
|
||||||
cursor, other_table.name
|
|
||||||
)
|
|
||||||
for constraint_column, constraint_table in relations.values():
|
|
||||||
if constraint_table == table_name and (
|
|
||||||
column_name is None or constraint_column == column_name
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def alter_db_table(
|
|
||||||
self, model, old_db_table, new_db_table, disable_constraints=True
|
|
||||||
):
|
|
||||||
if (
|
|
||||||
not self.connection.features.supports_atomic_references_rename
|
|
||||||
and disable_constraints
|
|
||||||
and self._is_referenced_by_fk_constraint(old_db_table)
|
|
||||||
):
|
|
||||||
if self.connection.in_atomic_block:
|
|
||||||
raise NotSupportedError(
|
|
||||||
(
|
|
||||||
"Renaming the %r table while in a transaction is not "
|
|
||||||
"supported on SQLite < 3.26 because it would break referential "
|
|
||||||
"integrity. Try adding `atomic = False` to the Migration class."
|
|
||||||
)
|
|
||||||
% old_db_table
|
|
||||||
)
|
|
||||||
self.connection.enable_constraint_checking()
|
|
||||||
super().alter_db_table(model, old_db_table, new_db_table)
|
|
||||||
self.connection.disable_constraint_checking()
|
|
||||||
else:
|
|
||||||
super().alter_db_table(model, old_db_table, new_db_table)
|
|
||||||
|
|
||||||
def alter_field(self, model, old_field, new_field, strict=False):
|
|
||||||
if not self._field_should_be_altered(old_field, new_field):
|
|
||||||
return
|
|
||||||
old_field_name = old_field.name
|
|
||||||
table_name = model._meta.db_table
|
|
||||||
_, old_column_name = old_field.get_attname_column()
|
|
||||||
if (
|
|
||||||
new_field.name != old_field_name
|
|
||||||
and not self.connection.features.supports_atomic_references_rename
|
|
||||||
and self._is_referenced_by_fk_constraint(
|
|
||||||
table_name, old_column_name, ignore_self=True
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if self.connection.in_atomic_block:
|
|
||||||
raise NotSupportedError(
|
|
||||||
(
|
|
||||||
"Renaming the %r.%r column while in a transaction is not "
|
|
||||||
"supported on SQLite < 3.26 because it would break referential "
|
|
||||||
"integrity. Try adding `atomic = False` to the Migration class."
|
|
||||||
)
|
|
||||||
% (model._meta.db_table, old_field_name)
|
|
||||||
)
|
|
||||||
with atomic(self.connection.alias):
|
|
||||||
super().alter_field(model, old_field, new_field, strict=strict)
|
|
||||||
# Follow SQLite's documented procedure for performing changes
|
|
||||||
# that don't affect the on-disk content.
|
|
||||||
# https://sqlite.org/lang_altertable.html#otheralter
|
|
||||||
with self.connection.cursor() as cursor:
|
|
||||||
schema_version = cursor.execute("PRAGMA schema_version").fetchone()[
|
|
||||||
0
|
|
||||||
]
|
|
||||||
cursor.execute("PRAGMA writable_schema = 1")
|
|
||||||
references_template = ' REFERENCES "%s" ("%%s") ' % table_name
|
|
||||||
new_column_name = new_field.get_attname_column()[1]
|
|
||||||
search = references_template % old_column_name
|
|
||||||
replacement = references_template % new_column_name
|
|
||||||
cursor.execute(
|
|
||||||
"UPDATE sqlite_master SET sql = replace(sql, %s, %s)",
|
|
||||||
(search, replacement),
|
|
||||||
)
|
|
||||||
cursor.execute("PRAGMA schema_version = %d" % (schema_version + 1))
|
|
||||||
cursor.execute("PRAGMA writable_schema = 0")
|
|
||||||
# The integrity check will raise an exception and rollback
|
|
||||||
# the transaction if the sqlite_master updates corrupt the
|
|
||||||
# database.
|
|
||||||
cursor.execute("PRAGMA integrity_check")
|
|
||||||
# Perform a VACUUM to refresh the database representation from
|
|
||||||
# the sqlite_master table.
|
|
||||||
with self.connection.cursor() as cursor:
|
|
||||||
cursor.execute("VACUUM")
|
|
||||||
else:
|
|
||||||
super().alter_field(model, old_field, new_field, strict=strict)
|
|
||||||
|
|
||||||
def _remake_table(
|
def _remake_table(
|
||||||
self, model, create_field=None, delete_field=None, alter_fields=None
|
self, model, create_field=None, delete_field=None, alter_fields=None
|
||||||
):
|
):
|
||||||
@ -358,7 +258,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|||||||
new_model,
|
new_model,
|
||||||
new_model._meta.db_table,
|
new_model._meta.db_table,
|
||||||
model._meta.db_table,
|
model._meta.db_table,
|
||||||
disable_constraints=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run deferred SQL on correct table
|
# Run deferred SQL on correct table
|
||||||
@ -458,8 +357,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
|||||||
# Use "ALTER TABLE ... RENAME COLUMN" if only the column name
|
# Use "ALTER TABLE ... RENAME COLUMN" if only the column name
|
||||||
# changed and there aren't any constraints.
|
# changed and there aren't any constraints.
|
||||||
if (
|
if (
|
||||||
self.connection.features.can_alter_table_rename_column
|
old_field.column != new_field.column
|
||||||
and old_field.column != new_field.column
|
|
||||||
and self.column_sql(model, old_field) == self.column_sql(model, new_field)
|
and self.column_sql(model, old_field) == self.column_sql(model, new_field)
|
||||||
and not (
|
and not (
|
||||||
old_field.remote_field
|
old_field.remote_field
|
||||||
|
@ -59,7 +59,7 @@ Database Library Requirements Supported Versions Notes
|
|||||||
PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS.
|
PostgreSQL GEOS, GDAL, PROJ, PostGIS 12+ Requires PostGIS.
|
||||||
MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality <mysql-spatial-limitations>`.
|
MySQL GEOS, GDAL 8.0.11+ :ref:`Limited functionality <mysql-spatial-limitations>`.
|
||||||
Oracle GEOS, GDAL 19+ XE not supported.
|
Oracle GEOS, GDAL 19+ XE not supported.
|
||||||
SQLite GEOS, GDAL, PROJ, SpatiaLite 3.21.0+ Requires SpatiaLite 4.3+
|
SQLite GEOS, GDAL, PROJ, SpatiaLite 3.27.0+ Requires SpatiaLite 4.3+
|
||||||
================== ============================== ================== =========================================
|
================== ============================== ================== =========================================
|
||||||
|
|
||||||
See also `this comparison matrix`__ on the OSGeo Wiki for
|
See also `this comparison matrix`__ on the OSGeo Wiki for
|
||||||
|
@ -792,7 +792,7 @@ appropriate typecasting.
|
|||||||
SQLite notes
|
SQLite notes
|
||||||
============
|
============
|
||||||
|
|
||||||
Django supports SQLite 3.21.0 and later.
|
Django supports SQLite 3.27.0 and later.
|
||||||
|
|
||||||
SQLite_ provides an excellent development alternative for applications that
|
SQLite_ provides an excellent development alternative for applications that
|
||||||
are predominantly read-only or require a smaller installation footprint. As
|
are predominantly read-only or require a smaller installation footprint. As
|
||||||
|
@ -2411,7 +2411,7 @@ On databases that support it (all but Oracle), setting the ``ignore_conflicts``
|
|||||||
parameter to ``True`` tells the database to ignore failure to insert any rows
|
parameter to ``True`` tells the database to ignore failure to insert any rows
|
||||||
that fail constraints such as duplicate unique values.
|
that fail constraints such as duplicate unique values.
|
||||||
|
|
||||||
On databases that support it (all except Oracle and SQLite < 3.24), setting the
|
On databases that support it (all except Oracle), setting the
|
||||||
``update_conflicts`` parameter to ``True``, tells the database to update
|
``update_conflicts`` parameter to ``True``, tells the database to update
|
||||||
``update_fields`` when a row insertion fails on conflicts. On PostgreSQL and
|
``update_fields`` when a row insertion fails on conflicts. On PostgreSQL and
|
||||||
SQLite, in addition to ``update_fields``, a list of ``unique_fields`` that may
|
SQLite, in addition to ``update_fields``, a list of ``unique_fields`` that may
|
||||||
|
@ -566,6 +566,8 @@ Miscellaneous
|
|||||||
* The ``AlreadyRegistered`` and ``NotRegistered`` exceptions are moved from
|
* The ``AlreadyRegistered`` and ``NotRegistered`` exceptions are moved from
|
||||||
``django.contrib.admin.sites`` to ``django.contrib.admin.exceptions``.
|
``django.contrib.admin.sites`` to ``django.contrib.admin.exceptions``.
|
||||||
|
|
||||||
|
* The minimum supported version of SQLite is increased from 3.21.0 to 3.27.0.
|
||||||
|
|
||||||
.. _deprecated-features-5.0:
|
.. _deprecated-features-5.0:
|
||||||
|
|
||||||
Features deprecated in 5.0
|
Features deprecated in 5.0
|
||||||
|
@ -7,17 +7,12 @@ from pathlib import Path
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.db import NotSupportedError, connection, transaction
|
from django.db import NotSupportedError, connection, transaction
|
||||||
from django.db.models import Aggregate, Avg, CharField, StdDev, Sum, Variance
|
from django.db.models import Aggregate, Avg, StdDev, Sum, Variance
|
||||||
from django.db.utils import ConnectionHandler
|
from django.db.utils import ConnectionHandler
|
||||||
from django.test import (
|
from django.test import TestCase, TransactionTestCase, override_settings
|
||||||
TestCase,
|
|
||||||
TransactionTestCase,
|
|
||||||
override_settings,
|
|
||||||
skipIfDBFeature,
|
|
||||||
)
|
|
||||||
from django.test.utils import isolate_apps
|
from django.test.utils import isolate_apps
|
||||||
|
|
||||||
from ..models import Author, Item, Object, Square
|
from ..models import Item, Object, Square
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(connection.vendor == "sqlite", "SQLite tests")
|
@unittest.skipUnless(connection.vendor == "sqlite", "SQLite tests")
|
||||||
@ -106,9 +101,9 @@ class Tests(TestCase):
|
|||||||
connections["default"].close()
|
connections["default"].close()
|
||||||
self.assertTrue(os.path.isfile(os.path.join(tmp, "test.db")))
|
self.assertTrue(os.path.isfile(os.path.join(tmp, "test.db")))
|
||||||
|
|
||||||
@mock.patch.object(connection, "get_database_version", return_value=(3, 20))
|
@mock.patch.object(connection, "get_database_version", return_value=(3, 26))
|
||||||
def test_check_database_version_supported(self, mocked_get_database_version):
|
def test_check_database_version_supported(self, mocked_get_database_version):
|
||||||
msg = "SQLite 3.21 or later is required (found 3.20)."
|
msg = "SQLite 3.27 or later is required (found 3.26)."
|
||||||
with self.assertRaisesMessage(NotSupportedError, msg):
|
with self.assertRaisesMessage(NotSupportedError, msg):
|
||||||
connection.check_database_version_supported()
|
connection.check_database_version_supported()
|
||||||
self.assertTrue(mocked_get_database_version.called)
|
self.assertTrue(mocked_get_database_version.called)
|
||||||
@ -167,39 +162,6 @@ class SchemaTests(TransactionTestCase):
|
|||||||
self.assertFalse(constraint_checks_enabled())
|
self.assertFalse(constraint_checks_enabled())
|
||||||
self.assertTrue(constraint_checks_enabled())
|
self.assertTrue(constraint_checks_enabled())
|
||||||
|
|
||||||
@skipIfDBFeature("supports_atomic_references_rename")
|
|
||||||
def test_field_rename_inside_atomic_block(self):
|
|
||||||
"""
|
|
||||||
NotImplementedError is raised when a model field rename is attempted
|
|
||||||
inside an atomic block.
|
|
||||||
"""
|
|
||||||
new_field = CharField(max_length=255, unique=True)
|
|
||||||
new_field.set_attributes_from_name("renamed")
|
|
||||||
msg = (
|
|
||||||
"Renaming the 'backends_author'.'name' column while in a "
|
|
||||||
"transaction is not supported on SQLite < 3.26 because it would "
|
|
||||||
"break referential integrity. Try adding `atomic = False` to the "
|
|
||||||
"Migration class."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(NotSupportedError, msg):
|
|
||||||
with connection.schema_editor(atomic=True) as editor:
|
|
||||||
editor.alter_field(Author, Author._meta.get_field("name"), new_field)
|
|
||||||
|
|
||||||
@skipIfDBFeature("supports_atomic_references_rename")
|
|
||||||
def test_table_rename_inside_atomic_block(self):
|
|
||||||
"""
|
|
||||||
NotImplementedError is raised when a table rename is attempted inside
|
|
||||||
an atomic block.
|
|
||||||
"""
|
|
||||||
msg = (
|
|
||||||
"Renaming the 'backends_author' table while in a transaction is "
|
|
||||||
"not supported on SQLite < 3.26 because it would break referential "
|
|
||||||
"integrity. Try adding `atomic = False` to the Migration class."
|
|
||||||
)
|
|
||||||
with self.assertRaisesMessage(NotSupportedError, msg):
|
|
||||||
with connection.schema_editor(atomic=True) as editor:
|
|
||||||
editor.alter_db_table(Author, "backends_author", "renamed_table")
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(connection.vendor == "sqlite", "Test only for SQLite")
|
@unittest.skipUnless(connection.vendor == "sqlite", "Test only for SQLite")
|
||||||
@override_settings(DEBUG=True)
|
@override_settings(DEBUG=True)
|
||||||
|
@ -805,10 +805,7 @@ class OperationTests(OperationTestBase):
|
|||||||
)
|
)
|
||||||
# Migrate forwards
|
# Migrate forwards
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
atomic_rename = connection.features.supports_atomic_references_rename
|
new_state = self.apply_operations("test_rnmo", new_state, [operation])
|
||||||
new_state = self.apply_operations(
|
|
||||||
"test_rnmo", new_state, [operation], atomic=atomic_rename
|
|
||||||
)
|
|
||||||
# Test new state and database
|
# Test new state and database
|
||||||
self.assertNotIn(("test_rnmo", "pony"), new_state.models)
|
self.assertNotIn(("test_rnmo", "pony"), new_state.models)
|
||||||
self.assertIn(("test_rnmo", "horse"), new_state.models)
|
self.assertIn(("test_rnmo", "horse"), new_state.models)
|
||||||
@ -828,7 +825,7 @@ class OperationTests(OperationTestBase):
|
|||||||
)
|
)
|
||||||
# Migrate backwards
|
# Migrate backwards
|
||||||
original_state = self.unapply_operations(
|
original_state = self.unapply_operations(
|
||||||
"test_rnmo", project_state, [operation], atomic=atomic_rename
|
"test_rnmo", project_state, [operation]
|
||||||
)
|
)
|
||||||
# Test original state and database
|
# Test original state and database
|
||||||
self.assertIn(("test_rnmo", "pony"), original_state.models)
|
self.assertIn(("test_rnmo", "pony"), original_state.models)
|
||||||
@ -907,8 +904,7 @@ class OperationTests(OperationTestBase):
|
|||||||
self.assertFKNotExists(
|
self.assertFKNotExists(
|
||||||
"test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id")
|
"test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id")
|
||||||
)
|
)
|
||||||
atomic_rename = connection.features.supports_atomic_references_rename
|
with connection.schema_editor() as editor:
|
||||||
with connection.schema_editor(atomic=atomic_rename) as editor:
|
|
||||||
operation.database_forwards("test_rmwsrf", editor, project_state, new_state)
|
operation.database_forwards("test_rmwsrf", editor, project_state, new_state)
|
||||||
self.assertTableNotExists("test_rmwsrf_rider")
|
self.assertTableNotExists("test_rmwsrf_rider")
|
||||||
self.assertTableExists("test_rmwsrf_horserider")
|
self.assertTableExists("test_rmwsrf_horserider")
|
||||||
@ -922,7 +918,7 @@ class OperationTests(OperationTestBase):
|
|||||||
("test_rmwsrf_horserider", "id"),
|
("test_rmwsrf_horserider", "id"),
|
||||||
)
|
)
|
||||||
# And test reversal
|
# And test reversal
|
||||||
with connection.schema_editor(atomic=atomic_rename) as editor:
|
with connection.schema_editor() as editor:
|
||||||
operation.database_backwards(
|
operation.database_backwards(
|
||||||
"test_rmwsrf", editor, new_state, project_state
|
"test_rmwsrf", editor, new_state, project_state
|
||||||
)
|
)
|
||||||
@ -972,9 +968,7 @@ class OperationTests(OperationTestBase):
|
|||||||
self.assertFKNotExists(
|
self.assertFKNotExists(
|
||||||
"test_rmwsc_rider", ["pony_id"], ("test_rmwsc_shetlandpony", "id")
|
"test_rmwsc_rider", ["pony_id"], ("test_rmwsc_shetlandpony", "id")
|
||||||
)
|
)
|
||||||
with connection.schema_editor(
|
with connection.schema_editor() as editor:
|
||||||
atomic=connection.features.supports_atomic_references_rename
|
|
||||||
) as editor:
|
|
||||||
operation.database_forwards("test_rmwsc", editor, project_state, new_state)
|
operation.database_forwards("test_rmwsc", editor, project_state, new_state)
|
||||||
# Now we have a little horse table, not shetland pony
|
# Now we have a little horse table, not shetland pony
|
||||||
self.assertTableNotExists("test_rmwsc_shetlandpony")
|
self.assertTableNotExists("test_rmwsc_shetlandpony")
|
||||||
@ -1031,7 +1025,6 @@ class OperationTests(OperationTestBase):
|
|||||||
operations=[
|
operations=[
|
||||||
migrations.RenameModel("ReflexivePony", "ReflexivePony2"),
|
migrations.RenameModel("ReflexivePony", "ReflexivePony2"),
|
||||||
],
|
],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
Pony = project_state.apps.get_model(app_label, "ReflexivePony2")
|
Pony = project_state.apps.get_model(app_label, "ReflexivePony2")
|
||||||
pony = Pony.objects.create()
|
pony = Pony.objects.create()
|
||||||
@ -1070,7 +1063,6 @@ class OperationTests(OperationTestBase):
|
|||||||
operations=[
|
operations=[
|
||||||
migrations.RenameModel("Pony", "Pony2"),
|
migrations.RenameModel("Pony", "Pony2"),
|
||||||
],
|
],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
Pony = project_state.apps.get_model(app_label, "Pony2")
|
Pony = project_state.apps.get_model(app_label, "Pony2")
|
||||||
Rider = project_state.apps.get_model(app_label, "Rider")
|
Rider = project_state.apps.get_model(app_label, "Rider")
|
||||||
@ -1125,7 +1117,6 @@ class OperationTests(OperationTestBase):
|
|||||||
app_label_2,
|
app_label_2,
|
||||||
project_state,
|
project_state,
|
||||||
operations=[migrations.RenameModel("Rider", "Pony")],
|
operations=[migrations.RenameModel("Rider", "Pony")],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
m2m_table = f"{app_label_2}_pony_riders"
|
m2m_table = f"{app_label_2}_pony_riders"
|
||||||
@ -1146,7 +1137,6 @@ class OperationTests(OperationTestBase):
|
|||||||
app_label_2,
|
app_label_2,
|
||||||
project_state_2,
|
project_state_2,
|
||||||
operations=[migrations.RenameModel("Rider", "Pony")],
|
operations=[migrations.RenameModel("Rider", "Pony")],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
m2m_table = f"{app_label_2}_rider_riders"
|
m2m_table = f"{app_label_2}_rider_riders"
|
||||||
self.assertColumnExists(m2m_table, "to_rider_id")
|
self.assertColumnExists(m2m_table, "to_rider_id")
|
||||||
@ -1178,7 +1168,6 @@ class OperationTests(OperationTestBase):
|
|||||||
app_label,
|
app_label,
|
||||||
project_state,
|
project_state,
|
||||||
operations=[migrations.RenameModel("Pony", "PinkPony")],
|
operations=[migrations.RenameModel("Pony", "PinkPony")],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
Pony = new_state.apps.get_model(app_label, "PinkPony")
|
Pony = new_state.apps.get_model(app_label, "PinkPony")
|
||||||
Rider = new_state.apps.get_model(app_label, "Rider")
|
Rider = new_state.apps.get_model(app_label, "Rider")
|
||||||
@ -1219,7 +1208,6 @@ class OperationTests(OperationTestBase):
|
|||||||
operations=[
|
operations=[
|
||||||
migrations.RenameModel("Rider", "Rider2"),
|
migrations.RenameModel("Rider", "Rider2"),
|
||||||
],
|
],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
Pony = project_state.apps.get_model(app_label, "Pony")
|
Pony = project_state.apps.get_model(app_label, "Pony")
|
||||||
Rider = project_state.apps.get_model(app_label, "Rider2")
|
Rider = project_state.apps.get_model(app_label, "Rider2")
|
||||||
@ -1341,7 +1329,6 @@ class OperationTests(OperationTestBase):
|
|||||||
),
|
),
|
||||||
migrations.RenameModel(old_name="Rider", new_name="Jockey"),
|
migrations.RenameModel(old_name="Rider", new_name="Jockey"),
|
||||||
],
|
],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
Pony = project_state.apps.get_model(app_label, "Pony")
|
Pony = project_state.apps.get_model(app_label, "Pony")
|
||||||
Jockey = project_state.apps.get_model(app_label, "Jockey")
|
Jockey = project_state.apps.get_model(app_label, "Jockey")
|
||||||
@ -2042,13 +2029,12 @@ class OperationTests(OperationTestBase):
|
|||||||
second_state = first_state.clone()
|
second_state = first_state.clone()
|
||||||
operation = migrations.AlterModelTable(name="pony", table=None)
|
operation = migrations.AlterModelTable(name="pony", table=None)
|
||||||
operation.state_forwards(app_label, second_state)
|
operation.state_forwards(app_label, second_state)
|
||||||
atomic_rename = connection.features.supports_atomic_references_rename
|
with connection.schema_editor() as editor:
|
||||||
with connection.schema_editor(atomic=atomic_rename) as editor:
|
|
||||||
operation.database_forwards(app_label, editor, first_state, second_state)
|
operation.database_forwards(app_label, editor, first_state, second_state)
|
||||||
self.assertTableExists(new_m2m_table)
|
self.assertTableExists(new_m2m_table)
|
||||||
self.assertTableNotExists(original_m2m_table)
|
self.assertTableNotExists(original_m2m_table)
|
||||||
# And test reversal
|
# And test reversal
|
||||||
with connection.schema_editor(atomic=atomic_rename) as editor:
|
with connection.schema_editor() as editor:
|
||||||
operation.database_backwards(app_label, editor, second_state, first_state)
|
operation.database_backwards(app_label, editor, second_state, first_state)
|
||||||
self.assertTableExists(original_m2m_table)
|
self.assertTableExists(original_m2m_table)
|
||||||
self.assertTableNotExists(new_m2m_table)
|
self.assertTableNotExists(new_m2m_table)
|
||||||
@ -2988,7 +2974,6 @@ class OperationTests(OperationTestBase):
|
|||||||
"Pony", "id", models.CharField(primary_key=True, max_length=99)
|
"Pony", "id", models.CharField(primary_key=True, max_length=99)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
atomic=connection.features.supports_atomic_references_rename,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_rename_field(self):
|
def test_rename_field(self):
|
||||||
|
@ -2075,9 +2075,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
editor.create_model(Book)
|
editor.create_model(Book)
|
||||||
new_field = CharField(max_length=255, unique=True)
|
new_field = CharField(max_length=255, unique=True)
|
||||||
new_field.set_attributes_from_name("renamed")
|
new_field.set_attributes_from_name("renamed")
|
||||||
with connection.schema_editor(
|
with connection.schema_editor() as editor:
|
||||||
atomic=connection.features.supports_atomic_references_rename
|
|
||||||
) as editor:
|
|
||||||
editor.alter_field(Author, Author._meta.get_field("name"), new_field)
|
editor.alter_field(Author, Author._meta.get_field("name"), new_field)
|
||||||
# Ensure the foreign key reference was updated.
|
# Ensure the foreign key reference was updated.
|
||||||
self.assertForeignKeyExists(Book, "author_id", "schema_author", "renamed")
|
self.assertForeignKeyExists(Book, "author_id", "schema_author", "renamed")
|
||||||
@ -2122,9 +2120,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
new_field = IntegerField(db_default=1985)
|
new_field = IntegerField(db_default=1985)
|
||||||
new_field.set_attributes_from_name("renamed_year")
|
new_field.set_attributes_from_name("renamed_year")
|
||||||
new_field.model = AuthorDbDefault
|
new_field.model = AuthorDbDefault
|
||||||
with connection.schema_editor(
|
with connection.schema_editor() as editor:
|
||||||
atomic=connection.features.supports_atomic_references_rename
|
|
||||||
) as editor:
|
|
||||||
editor.alter_field(AuthorDbDefault, old_field, new_field, strict=True)
|
editor.alter_field(AuthorDbDefault, old_field, new_field, strict=True)
|
||||||
columns = self.column_classes(AuthorDbDefault)
|
columns = self.column_classes(AuthorDbDefault)
|
||||||
self.assertEqual(columns["renamed_year"][1].default, "1985")
|
self.assertEqual(columns["renamed_year"][1].default, "1985")
|
||||||
@ -3550,9 +3546,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
connection.features.introspected_field_types["CharField"],
|
connection.features.introspected_field_types["CharField"],
|
||||||
)
|
)
|
||||||
# Alter the table
|
# Alter the table
|
||||||
with connection.schema_editor(
|
with connection.schema_editor() as editor:
|
||||||
atomic=connection.features.supports_atomic_references_rename
|
|
||||||
) as editor:
|
|
||||||
editor.alter_db_table(Author, "schema_author", "schema_otherauthor")
|
editor.alter_db_table(Author, "schema_author", "schema_otherauthor")
|
||||||
Author._meta.db_table = "schema_otherauthor"
|
Author._meta.db_table = "schema_otherauthor"
|
||||||
columns = self.column_classes(Author)
|
columns = self.column_classes(Author)
|
||||||
@ -3563,9 +3557,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
# Ensure the foreign key reference was updated
|
# Ensure the foreign key reference was updated
|
||||||
self.assertForeignKeyExists(Book, "author_id", "schema_otherauthor")
|
self.assertForeignKeyExists(Book, "author_id", "schema_otherauthor")
|
||||||
# Alter the table again
|
# Alter the table again
|
||||||
with connection.schema_editor(
|
with connection.schema_editor() as editor:
|
||||||
atomic=connection.features.supports_atomic_references_rename
|
|
||||||
) as editor:
|
|
||||||
editor.alter_db_table(Author, "schema_otherauthor", "schema_author")
|
editor.alter_db_table(Author, "schema_otherauthor", "schema_author")
|
||||||
# Ensure the table is still there
|
# Ensure the table is still there
|
||||||
Author._meta.db_table = "schema_author"
|
Author._meta.db_table = "schema_author"
|
||||||
@ -5130,8 +5122,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
editor.add_field(Book, author)
|
editor.add_field(Book, author)
|
||||||
|
|
||||||
def test_rename_table_renames_deferred_sql_references(self):
|
def test_rename_table_renames_deferred_sql_references(self):
|
||||||
atomic_rename = connection.features.supports_atomic_references_rename
|
with connection.schema_editor() as editor:
|
||||||
with connection.schema_editor(atomic=atomic_rename) as editor:
|
|
||||||
editor.create_model(Author)
|
editor.create_model(Author)
|
||||||
editor.create_model(Book)
|
editor.create_model(Book)
|
||||||
editor.alter_db_table(Author, "schema_author", "schema_renamed_author")
|
editor.alter_db_table(Author, "schema_author", "schema_renamed_author")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user