Opened 6 years ago

Last modified 6 years ago

#29852 closed Bug

Infinite migrations when using SimpleLazyObject in field arguments — at Version 1

Reported by: Javier Buzzi Owned by: nobody
Component: Migrations Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description (last modified by Tim Graham)

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.

Change History (1)

comment:1 by Tim Graham, 6 years ago

Component: UncategorizedMigrations
Description: modified (diff)
Summary: SimpleLazyObject Causing Migrations to be created over and overInfinite migrations when using SimpleLazyObject in field arguments
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug

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.

Note: See TracTickets for help on using tickets.
Back to Top