mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +00:00
Fixed #35038 -- Created AlterConstraint operation.
This commit is contained in:
parent
b92511b474
commit
b82f80906a
@ -219,6 +219,7 @@ class MigrationAutodetector:
|
|||||||
self.generate_altered_unique_together()
|
self.generate_altered_unique_together()
|
||||||
self.generate_added_indexes()
|
self.generate_added_indexes()
|
||||||
self.generate_added_constraints()
|
self.generate_added_constraints()
|
||||||
|
self.generate_altered_constraints()
|
||||||
self.generate_altered_db_table()
|
self.generate_altered_db_table()
|
||||||
|
|
||||||
self._sort_migrations()
|
self._sort_migrations()
|
||||||
@ -1450,6 +1451,19 @@ class MigrationAutodetector:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _constraint_should_be_dropped_and_recreated(
|
||||||
|
self, old_constraint, new_constraint
|
||||||
|
):
|
||||||
|
old_path, old_args, old_kwargs = old_constraint.deconstruct()
|
||||||
|
new_path, new_args, new_kwargs = new_constraint.deconstruct()
|
||||||
|
|
||||||
|
for attr in old_constraint.non_db_attrs:
|
||||||
|
old_kwargs.pop(attr, None)
|
||||||
|
for attr in new_constraint.non_db_attrs:
|
||||||
|
new_kwargs.pop(attr, None)
|
||||||
|
|
||||||
|
return (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs)
|
||||||
|
|
||||||
def create_altered_constraints(self):
|
def create_altered_constraints(self):
|
||||||
option_name = operations.AddConstraint.option_name
|
option_name = operations.AddConstraint.option_name
|
||||||
for app_label, model_name in sorted(self.kept_model_keys):
|
for app_label, model_name in sorted(self.kept_model_keys):
|
||||||
@ -1461,14 +1475,41 @@ class MigrationAutodetector:
|
|||||||
|
|
||||||
old_constraints = old_model_state.options[option_name]
|
old_constraints = old_model_state.options[option_name]
|
||||||
new_constraints = new_model_state.options[option_name]
|
new_constraints = new_model_state.options[option_name]
|
||||||
add_constraints = [c for c in new_constraints if c not in old_constraints]
|
|
||||||
rem_constraints = [c for c in old_constraints if c not in new_constraints]
|
alt_constraints = []
|
||||||
|
alt_constraints_name = []
|
||||||
|
|
||||||
|
for old_c in old_constraints:
|
||||||
|
for new_c in new_constraints:
|
||||||
|
old_c_dec = old_c.deconstruct()
|
||||||
|
new_c_dec = new_c.deconstruct()
|
||||||
|
if (
|
||||||
|
old_c_dec != new_c_dec
|
||||||
|
and old_c.name == new_c.name
|
||||||
|
and not self._constraint_should_be_dropped_and_recreated(
|
||||||
|
old_c, new_c
|
||||||
|
)
|
||||||
|
):
|
||||||
|
alt_constraints.append(new_c)
|
||||||
|
alt_constraints_name.append(new_c.name)
|
||||||
|
|
||||||
|
add_constraints = [
|
||||||
|
c
|
||||||
|
for c in new_constraints
|
||||||
|
if c not in old_constraints and c.name not in alt_constraints_name
|
||||||
|
]
|
||||||
|
rem_constraints = [
|
||||||
|
c
|
||||||
|
for c in old_constraints
|
||||||
|
if c not in new_constraints and c.name not in alt_constraints_name
|
||||||
|
]
|
||||||
|
|
||||||
self.altered_constraints.update(
|
self.altered_constraints.update(
|
||||||
{
|
{
|
||||||
(app_label, model_name): {
|
(app_label, model_name): {
|
||||||
"added_constraints": add_constraints,
|
"added_constraints": add_constraints,
|
||||||
"removed_constraints": rem_constraints,
|
"removed_constraints": rem_constraints,
|
||||||
|
"altered_constraints": alt_constraints,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -1503,6 +1544,23 @@ class MigrationAutodetector:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_altered_constraints(self):
|
||||||
|
for (
|
||||||
|
app_label,
|
||||||
|
model_name,
|
||||||
|
), alt_constraints in self.altered_constraints.items():
|
||||||
|
dependencies = self._get_dependencies_for_model(app_label, model_name)
|
||||||
|
for constraint in alt_constraints["altered_constraints"]:
|
||||||
|
self.add_operation(
|
||||||
|
app_label,
|
||||||
|
operations.AlterConstraint(
|
||||||
|
model_name=model_name,
|
||||||
|
name=constraint.name,
|
||||||
|
constraint=constraint,
|
||||||
|
),
|
||||||
|
dependencies=dependencies,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_dependencies_for_foreign_key(app_label, model_name, field, project_state):
|
def _get_dependencies_for_foreign_key(app_label, model_name, field, project_state):
|
||||||
remote_field_model = None
|
remote_field_model = None
|
||||||
|
@ -2,6 +2,7 @@ from .fields import AddField, AlterField, RemoveField, RenameField
|
|||||||
from .models import (
|
from .models import (
|
||||||
AddConstraint,
|
AddConstraint,
|
||||||
AddIndex,
|
AddIndex,
|
||||||
|
AlterConstraint,
|
||||||
AlterIndexTogether,
|
AlterIndexTogether,
|
||||||
AlterModelManagers,
|
AlterModelManagers,
|
||||||
AlterModelOptions,
|
AlterModelOptions,
|
||||||
@ -36,6 +37,7 @@ __all__ = [
|
|||||||
"RenameField",
|
"RenameField",
|
||||||
"AddConstraint",
|
"AddConstraint",
|
||||||
"RemoveConstraint",
|
"RemoveConstraint",
|
||||||
|
"AlterConstraint",
|
||||||
"SeparateDatabaseAndState",
|
"SeparateDatabaseAndState",
|
||||||
"RunSQL",
|
"RunSQL",
|
||||||
"RunPython",
|
"RunPython",
|
||||||
|
@ -1230,6 +1230,12 @@ class AddConstraint(IndexOperation):
|
|||||||
and self.constraint.name == operation.name
|
and self.constraint.name == operation.name
|
||||||
):
|
):
|
||||||
return []
|
return []
|
||||||
|
if (
|
||||||
|
isinstance(operation, AlterConstraint)
|
||||||
|
and self.model_name_lower == operation.model_name_lower
|
||||||
|
and self.constraint.name == operation.name
|
||||||
|
):
|
||||||
|
return [AddConstraint(self.model_name, operation.constraint)]
|
||||||
return super().reduce(operation, app_label)
|
return super().reduce(operation, app_label)
|
||||||
|
|
||||||
|
|
||||||
@ -1274,3 +1280,51 @@ class RemoveConstraint(IndexOperation):
|
|||||||
@property
|
@property
|
||||||
def migration_name_fragment(self):
|
def migration_name_fragment(self):
|
||||||
return "remove_%s_%s" % (self.model_name_lower, self.name.lower())
|
return "remove_%s_%s" % (self.model_name_lower, self.name.lower())
|
||||||
|
|
||||||
|
|
||||||
|
class AlterConstraint(IndexOperation):
|
||||||
|
category = OperationCategory.ALTERATION
|
||||||
|
option_name = "constraints"
|
||||||
|
|
||||||
|
def __init__(self, model_name, name, constraint):
|
||||||
|
self.model_name = model_name
|
||||||
|
self.name = name
|
||||||
|
self.constraint = constraint
|
||||||
|
|
||||||
|
def state_forwards(self, app_label, state):
|
||||||
|
state.alter_constraint(
|
||||||
|
app_label, self.model_name_lower, self.name, self.constraint
|
||||||
|
)
|
||||||
|
|
||||||
|
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
return (
|
||||||
|
self.__class__.__name__,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
"model_name": self.model_name,
|
||||||
|
"name": self.name,
|
||||||
|
"constraint": self.constraint,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
return f"Alter constraint {self.name} on {self.model_name}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def migration_name_fragment(self):
|
||||||
|
return "alter_%s_%s" % (self.model_name_lower, self.constraint.name.lower())
|
||||||
|
|
||||||
|
def reduce(self, operation, app_label):
|
||||||
|
if (
|
||||||
|
isinstance(operation, (AlterConstraint, RemoveConstraint))
|
||||||
|
and self.model_name_lower == operation.model_name_lower
|
||||||
|
and self.name == operation.name
|
||||||
|
):
|
||||||
|
return [operation]
|
||||||
|
return super().reduce(operation, app_label)
|
||||||
|
@ -211,6 +211,14 @@ class ProjectState:
|
|||||||
model_state.options[option_name] = [obj for obj in objs if obj.name != obj_name]
|
model_state.options[option_name] = [obj for obj in objs if obj.name != obj_name]
|
||||||
self.reload_model(app_label, model_name, delay=True)
|
self.reload_model(app_label, model_name, delay=True)
|
||||||
|
|
||||||
|
def _alter_option(self, app_label, model_name, option_name, obj_name, alt_obj):
|
||||||
|
model_state = self.models[app_label, model_name]
|
||||||
|
objs = model_state.options[option_name]
|
||||||
|
model_state.options[option_name] = [
|
||||||
|
obj if obj.name != obj_name else alt_obj for obj in objs
|
||||||
|
]
|
||||||
|
self.reload_model(app_label, model_name, delay=True)
|
||||||
|
|
||||||
def add_index(self, app_label, model_name, index):
|
def add_index(self, app_label, model_name, index):
|
||||||
self._append_option(app_label, model_name, "indexes", index)
|
self._append_option(app_label, model_name, "indexes", index)
|
||||||
|
|
||||||
@ -237,6 +245,11 @@ class ProjectState:
|
|||||||
def remove_constraint(self, app_label, model_name, constraint_name):
|
def remove_constraint(self, app_label, model_name, constraint_name):
|
||||||
self._remove_option(app_label, model_name, "constraints", constraint_name)
|
self._remove_option(app_label, model_name, "constraints", constraint_name)
|
||||||
|
|
||||||
|
def alter_constraint(self, app_label, model_name, constraint_name, constraint):
|
||||||
|
self._alter_option(
|
||||||
|
app_label, model_name, "constraints", constraint_name, constraint
|
||||||
|
)
|
||||||
|
|
||||||
def add_field(self, app_label, model_name, name, field, preserve_default):
|
def add_field(self, app_label, model_name, name, field, preserve_default):
|
||||||
# If preserve default is off, don't use the default for future state.
|
# If preserve default is off, don't use the default for future state.
|
||||||
if not preserve_default:
|
if not preserve_default:
|
||||||
|
@ -23,6 +23,8 @@ class BaseConstraint:
|
|||||||
violation_error_code = None
|
violation_error_code = None
|
||||||
violation_error_message = None
|
violation_error_message = None
|
||||||
|
|
||||||
|
non_db_attrs = ("violation_error_code", "violation_error_message")
|
||||||
|
|
||||||
# RemovedInDjango60Warning: When the deprecation ends, replace with:
|
# RemovedInDjango60Warning: When the deprecation ends, replace with:
|
||||||
# def __init__(
|
# def __init__(
|
||||||
# self, *, name, violation_error_code=None, violation_error_message=None
|
# self, *, name, violation_error_code=None, violation_error_message=None
|
||||||
|
@ -278,6 +278,16 @@ the model with ``model_name``.
|
|||||||
|
|
||||||
Removes the constraint named ``name`` from the model with ``model_name``.
|
Removes the constraint named ``name`` from the model with ``model_name``.
|
||||||
|
|
||||||
|
``AlterConstraint``
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. versionadded:: 5.2
|
||||||
|
|
||||||
|
.. class:: AlterConstraint(model_name, name, constraint)
|
||||||
|
|
||||||
|
Alters the constraint named ``name`` of the model with ``model_name`` with the
|
||||||
|
new ``constraint`` without affecting the database.
|
||||||
|
|
||||||
Special Operations
|
Special Operations
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
@ -259,7 +259,8 @@ Management Commands
|
|||||||
Migrations
|
Migrations
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The new operation :class:`.AlterConstraint` is a no-op operation that alters
|
||||||
|
constraints without dropping and recreating constraints in the database.
|
||||||
|
|
||||||
Models
|
Models
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
@ -2969,6 +2969,71 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
["CreateModel", "AddField", "AddIndex"],
|
["CreateModel", "AddField", "AddIndex"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_alter_constraint(self):
|
||||||
|
book_constraint = models.CheckConstraint(
|
||||||
|
condition=models.Q(title__contains="title"),
|
||||||
|
name="title_contains_title",
|
||||||
|
)
|
||||||
|
book_altered_constraint = models.CheckConstraint(
|
||||||
|
condition=models.Q(title__contains="title"),
|
||||||
|
name="title_contains_title",
|
||||||
|
violation_error_code="error_code",
|
||||||
|
)
|
||||||
|
author_altered_constraint = models.CheckConstraint(
|
||||||
|
condition=models.Q(name__contains="Bob"),
|
||||||
|
name="name_contains_bob",
|
||||||
|
violation_error_message="Name doesn't contain Bob",
|
||||||
|
)
|
||||||
|
|
||||||
|
book_check_constraint = copy.deepcopy(self.book)
|
||||||
|
book_check_constraint_with_error_message = copy.deepcopy(self.book)
|
||||||
|
author_name_check_constraint_with_error_message = copy.deepcopy(
|
||||||
|
self.author_name_check_constraint
|
||||||
|
)
|
||||||
|
|
||||||
|
book_check_constraint.options = {"constraints": [book_constraint]}
|
||||||
|
book_check_constraint_with_error_message.options = {
|
||||||
|
"constraints": [book_altered_constraint]
|
||||||
|
}
|
||||||
|
author_name_check_constraint_with_error_message.options = {
|
||||||
|
"constraints": [author_altered_constraint]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = self.get_changes(
|
||||||
|
[self.author_name_check_constraint, book_check_constraint],
|
||||||
|
[
|
||||||
|
author_name_check_constraint_with_error_message,
|
||||||
|
book_check_constraint_with_error_message,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNumberMigrations(changes, "testapp", 1)
|
||||||
|
self.assertOperationTypes(changes, "testapp", 0, ["AlterConstraint"])
|
||||||
|
self.assertOperationAttributes(
|
||||||
|
changes,
|
||||||
|
"testapp",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
model_name="author",
|
||||||
|
name="name_contains_bob",
|
||||||
|
constraint=author_altered_constraint,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNumberMigrations(changes, "otherapp", 1)
|
||||||
|
self.assertOperationTypes(changes, "otherapp", 0, ["AlterConstraint"])
|
||||||
|
self.assertOperationAttributes(
|
||||||
|
changes,
|
||||||
|
"otherapp",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
model_name="book",
|
||||||
|
name="title_contains_title",
|
||||||
|
constraint=book_altered_constraint,
|
||||||
|
)
|
||||||
|
self.assertMigrationDependencies(
|
||||||
|
changes, "otherapp", 0, [("testapp", "auto_1")]
|
||||||
|
)
|
||||||
|
|
||||||
def test_remove_constraints(self):
|
def test_remove_constraints(self):
|
||||||
"""Test change detection of removed constraints."""
|
"""Test change detection of removed constraints."""
|
||||||
changes = self.get_changes(
|
changes = self.get_changes(
|
||||||
|
@ -4366,6 +4366,81 @@ class OperationTests(OperationTestBase):
|
|||||||
{"model_name": "Pony", "name": "test_remove_constraint_pony_pink_gt_2"},
|
{"model_name": "Pony", "name": "test_remove_constraint_pony_pink_gt_2"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_alter_constraint(self):
|
||||||
|
constraint = models.UniqueConstraint(
|
||||||
|
fields=["pink"], name="test_alter_constraint_pony_fields_uq"
|
||||||
|
)
|
||||||
|
project_state = self.set_up_test_model(
|
||||||
|
"test_alterconstraint", constraints=[constraint]
|
||||||
|
)
|
||||||
|
|
||||||
|
new_state = project_state.clone()
|
||||||
|
violation_error_message = "Pink isn't unique"
|
||||||
|
uq_constraint = models.UniqueConstraint(
|
||||||
|
fields=["pink"],
|
||||||
|
name="test_alter_constraint_pony_fields_uq",
|
||||||
|
violation_error_message=violation_error_message,
|
||||||
|
)
|
||||||
|
uq_operation = migrations.AlterConstraint(
|
||||||
|
"Pony", "test_alter_constraint_pony_fields_uq", uq_constraint
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
uq_operation.describe(),
|
||||||
|
"Alter constraint test_alter_constraint_pony_fields_uq on Pony",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
uq_operation.formatted_description(),
|
||||||
|
"~ Alter constraint test_alter_constraint_pony_fields_uq on Pony",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
uq_operation.migration_name_fragment,
|
||||||
|
"alter_pony_test_alter_constraint_pony_fields_uq",
|
||||||
|
)
|
||||||
|
|
||||||
|
uq_operation.state_forwards("test_alterconstraint", new_state)
|
||||||
|
self.assertEqual(
|
||||||
|
project_state.models["test_alterconstraint", "pony"]
|
||||||
|
.options["constraints"][0]
|
||||||
|
.violation_error_message,
|
||||||
|
"Constraint “%(name)s” is violated.",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
new_state.models["test_alterconstraint", "pony"]
|
||||||
|
.options["constraints"][0]
|
||||||
|
.violation_error_message,
|
||||||
|
violation_error_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
with connection.schema_editor() as editor, self.assertNumQueries(0):
|
||||||
|
uq_operation.database_forwards(
|
||||||
|
"test_alterconstraint", editor, project_state, new_state
|
||||||
|
)
|
||||||
|
self.assertConstraintExists(
|
||||||
|
"test_alterconstraint_pony",
|
||||||
|
"test_alter_constraint_pony_fields_uq",
|
||||||
|
value=False,
|
||||||
|
)
|
||||||
|
with connection.schema_editor() as editor, self.assertNumQueries(0):
|
||||||
|
uq_operation.database_backwards(
|
||||||
|
"test_alterconstraint", editor, project_state, new_state
|
||||||
|
)
|
||||||
|
self.assertConstraintExists(
|
||||||
|
"test_alterconstraint_pony",
|
||||||
|
"test_alter_constraint_pony_fields_uq",
|
||||||
|
value=False,
|
||||||
|
)
|
||||||
|
definition = uq_operation.deconstruct()
|
||||||
|
self.assertEqual(definition[0], "AlterConstraint")
|
||||||
|
self.assertEqual(definition[1], [])
|
||||||
|
self.assertEqual(
|
||||||
|
definition[2],
|
||||||
|
{
|
||||||
|
"model_name": "Pony",
|
||||||
|
"name": "test_alter_constraint_pony_fields_uq",
|
||||||
|
"constraint": uq_constraint,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_add_partial_unique_constraint(self):
|
def test_add_partial_unique_constraint(self):
|
||||||
project_state = self.set_up_test_model("test_addpartialuniqueconstraint")
|
project_state = self.set_up_test_model("test_addpartialuniqueconstraint")
|
||||||
partial_unique_constraint = models.UniqueConstraint(
|
partial_unique_constraint = models.UniqueConstraint(
|
||||||
|
@ -1232,6 +1232,80 @@ class OptimizerTests(OptimizerTestBase):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_multiple_alter_constraints(self):
|
||||||
|
gt_constraint_violation_msg_added = models.CheckConstraint(
|
||||||
|
condition=models.Q(pink__gt=2),
|
||||||
|
name="pink_gt_2",
|
||||||
|
violation_error_message="ERROR",
|
||||||
|
)
|
||||||
|
gt_constraint_violation_msg_altered = models.CheckConstraint(
|
||||||
|
condition=models.Q(pink__gt=2),
|
||||||
|
name="pink_gt_2",
|
||||||
|
violation_error_message="error",
|
||||||
|
)
|
||||||
|
self.assertOptimizesTo(
|
||||||
|
[
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony", "pink_gt_2", gt_constraint_violation_msg_added
|
||||||
|
),
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony", "pink_gt_2", gt_constraint_violation_msg_altered
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony", "pink_gt_2", gt_constraint_violation_msg_altered
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
other_constraint_violation_msg = models.CheckConstraint(
|
||||||
|
condition=models.Q(weight__gt=3),
|
||||||
|
name="pink_gt_3",
|
||||||
|
violation_error_message="error",
|
||||||
|
)
|
||||||
|
self.assertDoesNotOptimize(
|
||||||
|
[
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony", "pink_gt_2", gt_constraint_violation_msg_added
|
||||||
|
),
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony", "pink_gt_3", other_constraint_violation_msg
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_alter_remove_constraint(self):
|
||||||
|
self.assertOptimizesTo(
|
||||||
|
[
|
||||||
|
migrations.AlterConstraint(
|
||||||
|
"Pony",
|
||||||
|
"pink_gt_2",
|
||||||
|
models.CheckConstraint(
|
||||||
|
condition=models.Q(pink__gt=2), name="pink_gt_2"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RemoveConstraint("Pony", "pink_gt_2"),
|
||||||
|
],
|
||||||
|
[migrations.RemoveConstraint("Pony", "pink_gt_2")],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_add_alter_constraint(self):
|
||||||
|
constraint = models.CheckConstraint(
|
||||||
|
condition=models.Q(pink__gt=2), name="pink_gt_2"
|
||||||
|
)
|
||||||
|
constraint_with_error = models.CheckConstraint(
|
||||||
|
condition=models.Q(pink__gt=2),
|
||||||
|
name="pink_gt_2",
|
||||||
|
violation_error_message="error",
|
||||||
|
)
|
||||||
|
self.assertOptimizesTo(
|
||||||
|
[
|
||||||
|
migrations.AddConstraint("Pony", constraint),
|
||||||
|
migrations.AlterConstraint("Pony", "pink_gt_2", constraint_with_error),
|
||||||
|
],
|
||||||
|
[migrations.AddConstraint("Pony", constraint_with_error)],
|
||||||
|
)
|
||||||
|
|
||||||
def test_create_model_add_index(self):
|
def test_create_model_add_index(self):
|
||||||
self.assertOptimizesTo(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
|
Loading…
Reference in New Issue
Block a user