Fixed #35234 -- Added system checks for invalid model field names in ExclusionConstraint.expressions.

This commit is contained in:
Simon Charette 2024-02-19 00:20:13 -05:00 committed by Mariusz Felisiak
parent 0fb104dda2
commit f82c67aa21
2 changed files with 64 additions and 0 deletions

View File

@ -77,6 +77,14 @@ class ExclusionConstraint(BaseConstraint):
expressions.append(expression)
return ExpressionList(*expressions).resolve_expression(query)
def _check(self, model, connection):
references = set()
for expr, _ in self.expressions:
if isinstance(expr, str):
expr = F(expr)
references.update(model._get_expr_references(expr))
return self._check_references(model, references)
def _get_condition_sql(self, compiler, schema_editor, query):
if self.condition is None:
return None

View File

@ -2,12 +2,17 @@ import datetime
from unittest import mock
from django.contrib.postgres.indexes import OpClass
from django.core.checks import Error
from django.core.exceptions import ValidationError
from django.db import IntegrityError, NotSupportedError, connection, transaction
from django.db.models import (
CASCADE,
CharField,
CheckConstraint,
DateField,
Deferrable,
F,
ForeignKey,
Func,
IntegerField,
Model,
@ -328,6 +333,57 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
include="invalid",
)
@isolate_apps("postgres_tests")
def test_check(self):
class Author(Model):
name = CharField(max_length=255)
alias = CharField(max_length=255)
class Meta:
app_label = "postgres_tests"
class Book(Model):
title = CharField(max_length=255)
published_date = DateField()
author = ForeignKey(Author, CASCADE)
class Meta:
app_label = "postgres_tests"
constraints = [
ExclusionConstraint(
name="exclude_check",
expressions=[
(F("title"), RangeOperators.EQUAL),
(F("published_date__year"), RangeOperators.EQUAL),
("published_date__month", RangeOperators.EQUAL),
(F("author__name"), RangeOperators.EQUAL),
("author__alias", RangeOperators.EQUAL),
("nonexistent", RangeOperators.EQUAL),
],
)
]
self.assertCountEqual(
Book.check(databases=self.databases),
[
Error(
"'constraints' refers to the nonexistent field 'nonexistent'.",
obj=Book,
id="models.E012",
),
Error(
"'constraints' refers to the joined field 'author__alias'.",
obj=Book,
id="models.E041",
),
Error(
"'constraints' refers to the joined field 'author__name'.",
obj=Book,
id="models.E041",
),
],
)
def test_repr(self):
constraint = ExclusionConstraint(
name="exclude_overlapping",