#29852: Infinite migrations when using SimpleLazyObject in field arguments
------------------------------+------------------------------------
Reporter: Javier Buzzi | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by Tim Graham):
* type: Uncategorized => Bug
* stage: Unreviewed => Accepted
* component: Uncategorized => Migrations
Old description:
> Reference: https://code.djangoproject.com/ticket/29772
>
> {{{
> from django.db import models
> from django.utils.functional import SimpleLazyObject
> from django.core.validators import MinValueValidator
> import datetime
>
> # Create your models here.
> class Thing(models.Model):
> day =
> models.DateTimeField(validators=[MinValueValidator(SimpleLazyObject(datetime.datetime.now))])
> }}}
>
> This works great right up until you try running migrations:
> 1. First time it creates a ` - Create model Thing`
> 2. Then you run the same command again and again, and will always
> generate a new migration ` - Alter field day on thing`
>
> {{{
> class Migration(migrations.Migration):
>
> dependencies = [
> ('app', '0005_auto_20181015_2203'),
> ]
>
> operations = [
> migrations.AlterField(
> model_name='thing',
> name='day',
> field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2018,
> 10, 15, 22, 3, 41, 390769))]),
> ),
> ]
> }}}
>
> The issue being that the `now()` is being evaluated and thus is always
> different.
>
> I got it 50% working with this diff:
>
> {{{
> diff --git a/django/db/migrations/serializer.py
> b/django/db/migrations/serializer.py
> index 911cf0f..ef2ad43 100644
> --- a/django/db/migrations/serializer.py
> +++ b/django/db/migrations/serializer.py
> @@ -12,7 +12,7 @@ import uuid
> from django.db import models
> from django.db.migrations.operations.base import Operation
> from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
> -from django.utils.functional import LazyObject, Promise
> +from django.utils.functional import LazyObject, Promise,
> SimpleLazyObject
> from django.utils.timezone import utc
> from django.utils.version import get_docs_version
>
> @@ -273,6 +273,8 @@ def serializer_factory(value):
> from django.db.migrations.writer import SettingsReference
> if isinstance(value, Promise):
> value = str(value)
> + elif isinstance(value, SimpleLazyObject):
> + value = value._setupfunc
> elif isinstance(value, LazyObject):
> # The unwrapped value is returned as the first item of the
> arguments
> # tuple.
> }}}
>
> Turns the migrations into:
>
> {{{
> class Migration(migrations.Migration):
>
> dependencies = [
> ('app', '0004_auto_20181015_2203'),
> ]
>
> operations = [
> migrations.AlterField(
> model_name='thing',
> name='day',
> field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime.now)]),
> ),
> ]
> }}}
>
> While it's a great improvement, it still generates a new one every time.
> I'm a little over my head with this one, this code is very dense. I could
> keep looking at it tomorrow, but i need someone to point me in the right
> direction + where in the world do i put the tests for this thing??
> Thanks.
New description:
{{{
from django.db import models
from django.utils.functional import SimpleLazyObject
from django.core.validators import MinValueValidator
import datetime
# Create your models here.
class Thing(models.Model):
day =
models.DateTimeField(validators=[MinValueValidator(SimpleLazyObject(datetime.datetime.now))])
}}}
This works great right up until you try running migrations:
1. First time it creates a ` - Create model Thing`
2. Then you run the same command again and again, and will always generate
a new migration ` - Alter field day on thing`
{{{
class Migration(migrations.Migration):
dependencies = [
('app', '0005_auto_20181015_2203'),
]
operations = [
migrations.AlterField(
model_name='thing',
name='day',
field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime(2018,
10, 15, 22, 3, 41, 390769))]),
),
]
}}}
The issue being that the `now()` is being evaluated and thus is always
different.
I got it 50% working with this diff:
{{{
diff --git a/django/db/migrations/serializer.py
b/django/db/migrations/serializer.py
index 911cf0f..ef2ad43 100644
--- a/django/db/migrations/serializer.py
+++ b/django/db/migrations/serializer.py
@@ -12,7 +12,7 @@ import uuid
from django.db import models
from django.db.migrations.operations.base import Operation
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
-from django.utils.functional import LazyObject, Promise
+from django.utils.functional import LazyObject, Promise, SimpleLazyObject
from django.utils.timezone import utc
from django.utils.version import get_docs_version
@@ -273,6 +273,8 @@ def serializer_factory(value):
from django.db.migrations.writer import SettingsReference
if isinstance(value, Promise):
value = str(value)
+ elif isinstance(value, SimpleLazyObject):
+ value = value._setupfunc
elif isinstance(value, LazyObject):
# The unwrapped value is returned as the first item of the
arguments
# tuple.
}}}
Turns the migrations into:
{{{
class Migration(migrations.Migration):
dependencies = [
('app', '0004_auto_20181015_2203'),
]
operations = [
migrations.AlterField(
model_name='thing',
name='day',
field=models.DateTimeField(validators=[django.core.validators.MinValueValidator(datetime.datetime.now)]),
),
]
}}}
While it's a great improvement, it still generates a new one every time.
I'm a little over my head with this one, this code is very dense. I could
keep looking at it tomorrow, but i need someone to point me in the right
direction + where in the world do i put the tests for this thing?? Thanks.
--
Comment:
I think the problem is this:
{{{
>>> a = SimpleLazyObject(datetime.datetime.now)
>>> b = SimpleLazyObject(datetime.datetime.now)
>>> a == b
}}}
The two must be considered equal to prevent infinite migrations. See
91f701f4fc324cd2feb7dbf151338a358ca0ea18 for a similar issue.
--
Ticket URL: <https://code.djangoproject.com/ticket/29852#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 post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/069.3c41f1e38114765387ced2800e83141a%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.