[Django] #35575: Model.full_clean() does not recalculate GeneratedFields prior to validating model constraints

6 views
Skip to first unread message

Django

unread,
Jul 3, 2024, 6:22:17 AM (3 days ago) Jul 3
to django-...@googlegroups.com
#35575: Model.full_clean() does not recalculate GeneratedFields prior to validating
model constraints
-------------------------------------+-------------------------------------
Reporter: Mark | Owner: Mark Gensler
Gensler |
Type: Bug | Status: assigned
Component: Database | Version: 5.0
layer (models, ORM) | Keywords: generatedfield
Severity: Normal | uniqueconstraint checkconstraint
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
`GeneratedField`s on a model instance are not recalculated during
`Model.full_clean()`. Therefore, if a `GeneratedField` is included in the
`*expressions` or `fields` parameters of a `UniqueConstraint` or
`CheckConstraint` in the instance's `Model._meta`, the values of the
`GeneratedField` attribute are not recalculated prior to calling
`BaseConstraint.validate()`.

Instead, the model instance's `GeneratedField` attribute will return the
value which corresponds to the most recent refresh of the instance from
the database. If the instance is new and has not yet been saved, the
following error is raised:

`AttributeError: Cannot read a generated field from an unsaved model.`

An `IntegrityError` is correctly raised when calling `Model.save()` if any
constraints involving `GeneratedField`s are violated.

Instead, the `GeneratedField`s should have their values recalculated as
part of `Model.full_clean()`.

This ticket was raised after fixing the bug in #35560 with PR
[https://github.com/django/django/pull/18309].
--
Ticket URL: <https://code.djangoproject.com/ticket/35575>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jul 3, 2024, 6:47:50 AM (2 days ago) Jul 3
to django-...@googlegroups.com
#35575: Model.full_clean() does not recalculate GeneratedFields prior to validating
model constraints
-------------------------------------+-------------------------------------
Reporter: Mark Gensler | Owner: Mark
| Gensler
Type: Bug | Status: assigned
Component: Database layer | Version: 5.0
(models, ORM) |
Severity: Normal | Resolution:
Keywords: generatedfield | Triage Stage:
uniqueconstraint checkconstraint | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Mark Gensler):

I'm happy to start working on this. I will raise a PR and add failing
tests to catch the bug shortly.

Before writing any code to address this, I'd like to discuss ''where''
best to apply the fix. I see two possible options:

1. Recalculate `GeneratedField` attribute values on the fly as part of
calling `Constraint.validate()`. This will leave the instance attributes
unchanged until `Model.save()` is called.
2. Recalculate `GeneratedField` attributes on the model instance during
`Model.full_clean()`, prior to calls to `Constraint.validate()`, and
irrespective of whether relevant constraints are present.

The second option would allow further checks against the new
`GeneratedField` value of the instance to be made during validation. On
the other hand, the first option would leave the instance's attribute
returning the value currently returned/stored by the database.

Which would be best?
--
Ticket URL: <https://code.djangoproject.com/ticket/35575#comment:1>

Django

unread,
Jul 3, 2024, 7:30:52 AM (2 days ago) Jul 3
to django-...@googlegroups.com
#35575: Model.full_clean() does not recalculate GeneratedFields prior to validating
model constraints
-------------------------------------+-------------------------------------
Reporter: Mark Gensler | Owner: Mark
| Gensler
Type: Bug | Status: assigned
Component: Database layer | Version: 5.0
(models, ORM) |
Severity: Normal | Resolution:
Keywords: generatedfield | Triage Stage:
uniqueconstraint checkconstraint | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Mark Gensler:

Old description:

> `GeneratedField`s on a model instance are not recalculated during
> `Model.full_clean()`. Therefore, if a `GeneratedField` is included in the
> `*expressions` or `fields` parameters of a `UniqueConstraint` or
> `CheckConstraint` in the instance's `Model._meta`, the values of the
> `GeneratedField` attribute are not recalculated prior to calling
> `BaseConstraint.validate()`.
>
> Instead, the model instance's `GeneratedField` attribute will return the
> value which corresponds to the most recent refresh of the instance from
> the database. If the instance is new and has not yet been saved, the
> following error is raised:
>
> `AttributeError: Cannot read a generated field from an unsaved model.`
>
> An `IntegrityError` is correctly raised when calling `Model.save()` if
> any constraints involving `GeneratedField`s are violated.
>
> Instead, the `GeneratedField`s should have their values recalculated as
> part of `Model.full_clean()`.
>
> This ticket was raised after fixing the bug in #35560 with PR
> [https://github.com/django/django/pull/18309].

New description:

`GeneratedField`s on a model instance are not recalculated during
`Model.full_clean()`. Therefore, if a `GeneratedField` is included in the
`*expressions` or `fields` parameters of a `UniqueConstraint` or
`CheckConstraint` in the instance's `Model._meta`, the new values of the
`GeneratedField` attribute are not uaed by `BaseConstraint.validate()`.

Instead, the model instance's `GeneratedField` attribute will return the
value which corresponds to the most recent refresh of the instance from
the database. If the instance is new and has not yet been saved, the
following error is raised:

`AttributeError: Cannot read a generated field from an unsaved model.`

An `IntegrityError` is correctly raised when calling `Model.save()` if any
constraints involving `GeneratedField`s are violated.

Instead, the recalculated values for `GeneratedField`s should be used when
checking constraints.

This ticket was raised after fixing the bug in #35560 with PR
[https://github.com/django/django/pull/18309].

--
--
Ticket URL: <https://code.djangoproject.com/ticket/35575#comment:2>

Django

unread,
Jul 3, 2024, 7:32:53 AM (2 days ago) Jul 3
to django-...@googlegroups.com
#35575: Model.full_clean() does not recalculate GeneratedFields prior to validating
model constraints
-------------------------------------+-------------------------------------
Reporter: Mark Gensler | Owner: Mark
| Gensler
Type: Bug | Status: assigned
Component: Database layer | Version: 5.0
(models, ORM) |
Severity: Normal | Resolution:
Keywords: generatedfield | Triage Stage:
uniqueconstraint checkconstraint | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Mark Gensler:

Old description:

> `GeneratedField`s on a model instance are not recalculated during
> `Model.full_clean()`. Therefore, if a `GeneratedField` is included in the
> `*expressions` or `fields` parameters of a `UniqueConstraint` or
> `CheckConstraint` in the instance's `Model._meta`, the new values of the
> `GeneratedField` attribute are not uaed by `BaseConstraint.validate()`.
>
> Instead, the model instance's `GeneratedField` attribute will return the
> value which corresponds to the most recent refresh of the instance from
> the database. If the instance is new and has not yet been saved, the
> following error is raised:
>
> `AttributeError: Cannot read a generated field from an unsaved model.`
>
> An `IntegrityError` is correctly raised when calling `Model.save()` if
> any constraints involving `GeneratedField`s are violated.
>
> Instead, the recalculated values for `GeneratedField`s should be used
> when checking constraints.
>
> This ticket was raised after fixing the bug in #35560 with PR
> [https://github.com/django/django/pull/18309].

New description:

`GeneratedField`s on a model instance are not recalculated during
`Model.full_clean()`. Therefore, if a `GeneratedField` is included in the
`*expressions` or `fields` parameters of a `UniqueConstraint` or
`CheckConstraint` in the instance's `Model._meta`, the new values of the
`GeneratedField` attribute are not used by `BaseConstraint.validate()`.

Instead, the model instance's `GeneratedField` attribute will return the
value which corresponds to the most recent refresh of the instance from
the database. If the instance is new and has not yet been saved, the
following error is raised:

`AttributeError: Cannot read a generated field from an unsaved model.`

An `IntegrityError` is correctly raised when calling `Model.save()` if any
constraints involving `GeneratedField`s are violated.

Instead, the recalculated values for `GeneratedField`s should be used when
checking constraints.

This ticket was raised after fixing the bug in #35560 with PR
[https://github.com/django/django/pull/18309].

--
--
Ticket URL: <https://code.djangoproject.com/ticket/35575#comment:3>
Reply all
Reply to author
Forward
0 new messages