#36518: full_clean crashes on model with both a CheckConstraint and a
GeneratedField with a Case expression (possible regression in Django 5.2)
-------------------------------------+-------------------------------------
Reporter: Olivier Dalang | Owner: (none)
Type: Uncategorized | Status: new
Component: Database layer | Version: 5.2
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Olivier Dalang:
Old description:
> Hello !
>
> I think I ran accross a regression when upgrading the a model containing
> a CheckConstraint and a GeneratedField (that are unrelated) from Django
> 5.1 to 5.2.
>
> Below is a simplified case that reproduces the issue.
>
> I found is a similar ticket: https://code.djangoproject.com/ticket/34871
> (affecting 4.2 already, and thus not flagged as regression, for a
> slightly simpler use case).
>
> That ticket links to recent PR which could quite likely has introduced
> this regression: https://github.com/django/django/pull/19218
>
> I'm not yet familiar with contributing to Django, and fear this is (way)
> above my level to fix, but please let me know if I can do anything to
> help fixing this, as it currently prevents us from upgrading.
>
> Cheers !!
>
> Olivier
>
> {{{#!python
> # models.py
>
> class MyModel(models.Model):
> class Meta:
> constraints = [
> models.CheckConstraint(name="age_valid",
> check=Q(age__lt=100)),
> ]
>
> age = models.IntegerField()
> is_old_enough = models.GeneratedField(
> expression=Case(
> When(
> age__gte=18,
> then=Value(True),
> ),
> default=Value(False),
> ),
> db_persist=True,
> output_field=models.BooleanField(),
> )
> }}}
>
> {{{#!python
> # tests.py
>
> class MyTests(TestCase):
> def test_fullclean(self):
> bob = MyModel.objects.create(age=17)
> bob.full_clean()
> }}}
>
> The test succeeds on Django 5.1, but fails on 5.2.
>
> {{{
> > uv run --with django==5.2.4 manage.py test
> Found 1 test(s).
> Creating test database for alias 'default'...
> System check identified no issues (0 silenced).
> E
> ======================================================================
> ERROR: test_fullclean (myproj.myapp.tests.MyTests)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/home/app/myproj/myapp/tests.py", line 8, in test_fullclean
> bob.full_clean()
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/base.py", line 1674, in full_clean
> self.validate_constraints(exclude=exclude)
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/base.py", line 1622, in validate_constraints
> constraint.validate(model_class, self, exclude=exclude, using=using)
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/constraints.py", line 261, in validate
> against = instance._get_field_expression_map(meta=model._meta,
> exclude=exclude)
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/base.py", line 1372, in
> _get_field_expression_map
> generated_field.expression.replace_expressions(replacements),
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/expressions.py", line 427, in
> replace_expressions
> [
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/expressions.py", line 428, in <listcomp>
> expr.replace_expressions(replacements) if expr else None
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/expressions.py", line 427, in
> replace_expressions
> [
> File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
> packages/django/db/models/expressions.py", line 428, in <listcomp>
> expr.replace_expressions(replacements) if expr else None
> AttributeError: 'Q' object has no attribute 'replace_expressions'
> ----------------------------------------------------------------------
> Ran 1 test in 0.003s
>
> FAILED (errors=1)
> Destroying test database for alias 'default'...
> }}}
>
> {{{
> > uv run --with django==5.1.11 manage.py test
> Found 1 test(s).
> Creating test database for alias 'default'...
> System check identified no issues (0 silenced).
> .
> ----------------------------------------------------------------------
> Ran 1 test in 0.002s
>
> OK
> Destroying test database for alias 'default'...
> }}}
New description:
Hello !
I think I ran accross a regression when upgrading a model containing a
CheckConstraint and a GeneratedField (that are unrelated) from Django 5.1
to 5.2.
Below is a simplified case that reproduces the issue.
I found is a similar ticket: https://code.djangoproject.com/ticket/34871
(affecting 4.2 already, and thus not flagged as regression, for a slightly
simpler use case).
That ticket links to recent PR which could quite likely has introduced
this regression: https://github.com/django/django/pull/19218
I'm not yet familiar with contributing to Django, and fear this is (way)
above my level to fix, but please let me know if I can do anything to help
fixing this, as it currently prevents us from upgrading.
Cheers !!
Olivier
{{{#!python
# models.py
class MyModel(models.Model):
class Meta:
constraints = [
models.CheckConstraint(name="age_valid",
check=Q(age__lt=100)),
]
age = models.IntegerField()
is_old_enough = models.GeneratedField(
expression=Case(
When(
age__gte=18,
then=Value(True),
),
default=Value(False),
),
db_persist=True,
output_field=models.BooleanField(),
)
}}}
{{{#!python
# tests.py
class MyTests(TestCase):
def test_fullclean(self):
bob = MyModel.objects.create(age=17)
bob.full_clean()
}}}
The test succeeds on Django 5.1, but fails on 5.2.
{{{
> uv run --with django==5.2.4 manage.py test
Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_fullclean (myproj.myapp.tests.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/app/myproj/myapp/tests.py", line 8, in test_fullclean
bob.full_clean()
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/base.py", line 1674, in full_clean
self.validate_constraints(exclude=exclude)
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/base.py", line 1622, in validate_constraints
constraint.validate(model_class, self, exclude=exclude, using=using)
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/constraints.py", line 261, in validate
against = instance._get_field_expression_map(meta=model._meta,
exclude=exclude)
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/base.py", line 1372, in
_get_field_expression_map
generated_field.expression.replace_expressions(replacements),
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/expressions.py", line 427, in
replace_expressions
[
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/expressions.py", line 428, in <listcomp>
expr.replace_expressions(replacements) if expr else None
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/expressions.py", line 427, in
replace_expressions
[
File "/home/cache/archive-v0/kkaSi9So5gXBgF1hYbu9X/lib/site-
packages/django/db/models/expressions.py", line 428, in <listcomp>
expr.replace_expressions(replacements) if expr else None
AttributeError: 'Q' object has no attribute 'replace_expressions'
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (errors=1)
Destroying test database for alias 'default'...
}}}
{{{
> uv run --with django==5.1.11 manage.py test
Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Destroying test database for alias 'default'...
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/36518#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019833010418-6f64b615-5ca1-45cb-af19-b9ed1b115301-000000%40eu-central-1.amazonses.com.