1
0
mirror of https://github.com/django/django.git synced 2025-01-18 06:12:23 +00:00

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) expressions.append(expression)
return ExpressionList(*expressions).resolve_expression(query) 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): def _get_condition_sql(self, compiler, schema_editor, query):
if self.condition is None: if self.condition is None:
return None return None

View File

@ -2,12 +2,17 @@ import datetime
from unittest import mock from unittest import mock
from django.contrib.postgres.indexes import OpClass from django.contrib.postgres.indexes import OpClass
from django.core.checks import Error
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import IntegrityError, NotSupportedError, connection, transaction from django.db import IntegrityError, NotSupportedError, connection, transaction
from django.db.models import ( from django.db.models import (
CASCADE,
CharField,
CheckConstraint, CheckConstraint,
DateField,
Deferrable, Deferrable,
F, F,
ForeignKey,
Func, Func,
IntegerField, IntegerField,
Model, Model,
@ -328,6 +333,57 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
include="invalid", 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): def test_repr(self):
constraint = ExclusionConstraint( constraint = ExclusionConstraint(
name="exclude_overlapping", name="exclude_overlapping",