Fixed #34060 -- Fixed migrations crash when adding check constraints with JSONField __exact lookup on Oracle.

This commit is contained in:
Albert Defler 2024-02-14 17:34:00 +00:00 committed by Mariusz Felisiak
parent 22285d366c
commit c991602ce5
5 changed files with 64 additions and 3 deletions

View File

@ -347,9 +347,10 @@ END;
def lookup_cast(self, lookup_type, internal_type=None):
if lookup_type in ("iexact", "icontains", "istartswith", "iendswith"):
return "UPPER(%s)"
if (
lookup_type != "isnull" and internal_type in ("BinaryField", "TextField")
) or (lookup_type == "exact" and internal_type == "JSONField"):
if lookup_type != "isnull" and internal_type in (
"BinaryField",
"TextField",
):
return "DBMS_LOB.SUBSTR(%s)"
return "%s"

View File

@ -106,6 +106,13 @@ class DatabaseFeatures(BaseDatabaseFeatures):
"test_group_by_nested_expression_with_params",
}
)
if not is_psycopg3:
expected_failures.update(
{
"constraints.tests.CheckConstraintTests."
"test_validate_jsonfield_exact",
}
)
return expected_failures
@cached_property

View File

@ -310,6 +310,11 @@ class JSONExact(lookups.Exact):
rhs %= tuple(func)
return rhs, rhs_params
def as_oracle(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
return f"JSON_EQUAL({lhs}, {rhs})", (*lhs_params, *rhs_params)
class JSONIContains(CaseInsensitiveMixin, lookups.IContains):
pass

View File

@ -365,6 +365,20 @@ class CheckConstraintTests(TestCase):
constraint_with_pk.validate(ChildModel, ChildModel(id=1, age=1))
constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=1), exclude={"pk"})
@skipUnlessDBFeature("supports_json_field")
def test_validate_jsonfield_exact(self):
data = {"release": "5.0.2", "version": "stable"}
json_exact_constraint = models.CheckConstraint(
check=models.Q(data__version="stable"),
name="only_stable_version",
)
json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data))
data = {"release": "5.0.2", "version": "not stable"}
msg = f"Constraint “{json_exact_constraint.name}” is violated."
with self.assertRaisesMessage(ValidationError, msg):
json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data))
class UniqueConstraintTests(TestCase):
@classmethod

View File

@ -2803,6 +2803,40 @@ class SchemaTests(TransactionTestCase):
DurationModel.objects.create(duration=datetime.timedelta(minutes=4))
DurationModel.objects.create(duration=datetime.timedelta(minutes=10))
@skipUnlessDBFeature(
"supports_column_check_constraints",
"can_introspect_check_constraints",
"supports_json_field",
)
@isolate_apps("schema")
def test_check_constraint_exact_jsonfield(self):
class JSONConstraintModel(Model):
data = JSONField()
class Meta:
app_label = "schema"
with connection.schema_editor() as editor:
editor.create_model(JSONConstraintModel)
self.isolated_local_models = [JSONConstraintModel]
constraint_name = "check_only_stable_version"
constraint = CheckConstraint(
check=Q(data__version="stable"),
name=constraint_name,
)
JSONConstraintModel._meta.constraints = [constraint]
with connection.schema_editor() as editor:
editor.add_constraint(JSONConstraintModel, constraint)
constraints = self.get_constraints(JSONConstraintModel._meta.db_table)
self.assertIn(constraint_name, constraints)
with self.assertRaises(IntegrityError), atomic():
JSONConstraintModel.objects.create(
data={"release": "5.0.2dev", "version": "dev"}
)
JSONConstraintModel.objects.create(
data={"release": "5.0.3", "version": "stable"}
)
@skipUnlessDBFeature(
"supports_column_check_constraints", "can_introspect_check_constraints"
)