summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2022-08-10 07:51:07 -0400
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-08-11 09:53:10 +0200
commit35911078fa40eb35859832987fedada76963c01e (patch)
tree01e9da78e83ef5f15e754319e3407bd85bbfb3b0
parent8533a6af8d937d487dd4a9f5c40ac98e111915a8 (diff)
Replaced Expression.replace_references() with .replace_expressions().
The latter allows for more generic use cases beyond the currently limited ones constraints validation has. Refs #28333, #30581.
-rw-r--r--django/contrib/postgres/constraints.py3
-rw-r--r--django/db/models/constraints.py11
-rw-r--r--django/db/models/expressions.py13
3 files changed, 17 insertions, 10 deletions
diff --git a/django/contrib/postgres/constraints.py b/django/contrib/postgres/constraints.py
index c67292f9f0..8b76de3c42 100644
--- a/django/contrib/postgres/constraints.py
+++ b/django/contrib/postgres/constraints.py
@@ -198,6 +198,7 @@ class ExclusionConstraint(BaseConstraint):
replacement_map = instance._get_field_value_map(
meta=model._meta, exclude=exclude
)
+ replacements = {F(field): value for field, value in replacement_map.items()}
lookups = []
for idx, (expression, operator) in enumerate(self.expressions):
if isinstance(expression, str):
@@ -210,7 +211,7 @@ class ExclusionConstraint(BaseConstraint):
for expr in expression.flatten():
if isinstance(expr, F) and expr.name in exclude:
return
- rhs_expression = expression.replace_references(replacement_map)
+ rhs_expression = expression.replace_expressions(replacements)
# Remove OpClass because it only has sense during the constraint
# creation.
if isinstance(expression, OpClass):
diff --git a/django/db/models/constraints.py b/django/db/models/constraints.py
index 8cf1f0ff20..49c7c91de9 100644
--- a/django/db/models/constraints.py
+++ b/django/db/models/constraints.py
@@ -332,11 +332,14 @@ class UniqueConstraint(BaseConstraint):
return
elif isinstance(expression, F) and expression.name in exclude:
return
- replacement_map = instance._get_field_value_map(
- meta=model._meta, exclude=exclude
- )
+ replacements = {
+ F(field): value
+ for field, value in instance._get_field_value_map(
+ meta=model._meta, exclude=exclude
+ ).items()
+ }
expressions = [
- Exact(expr, expr.replace_references(replacement_map))
+ Exact(expr, expr.replace_expressions(replacements))
for expr in self.expressions
]
queryset = queryset.filter(*expressions)
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 6f635f565a..7d21285118 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -389,12 +389,15 @@ class BaseExpression:
)
return clone
- def replace_references(self, references_map):
+ def replace_expressions(self, replacements):
+ if replacement := replacements.get(self):
+ return replacement
clone = self.copy()
+ source_expressions = clone.get_source_expressions()
clone.set_source_expressions(
[
- expr.replace_references(references_map)
- for expr in self.get_source_expressions()
+ expr.replace_expressions(replacements) if expr else None
+ for expr in source_expressions
]
)
return clone
@@ -808,8 +811,8 @@ class F(Combinable):
):
return query.resolve_ref(self.name, allow_joins, reuse, summarize)
- def replace_references(self, references_map):
- return references_map.get(self.name, self)
+ def replace_expressions(self, replacements):
+ return replacements.get(self, self)
def asc(self, **kwargs):
return OrderBy(self, **kwargs)