#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:  1             |      Needs documentation:  0
  Needs tests:  0             |  Patch needs improvement:  1
Easy pickings:  0             |                    UI/UX:  0
------------------------------+------------------------------------

Comment (by Simon Charette):

 The more I think of it the more I feel like the infinite migrations here
 are expected and this is misuse of `SimpleLazyObject`.

 `SimpleLazyObject(foo)` is really just a way of saying ''execute `foo`  on
 first access but not now'' as demonstrated by the following snippet

 {{{#!python
 In [1]: from datetime import datetime

 In [2]: from django.utils.functional import SimpleLazyObject

 In [3]: lazy_now = SimpleLazyObject(datetime.now)

 In [4]: lazy_now.strftime("%Y-%m-%d %H:%M:%S")
 Out[4]: '2018-10-16 15:26:54'

 In [5]: import time; time.sleep(1)

 In [6]: lazy_now.strftime("%Y-%m-%d %H:%M:%S")
 Out[6]: '2018-10-16 15:26:54'
 }}}

 In this sense `MinValueValidator(SimpleLazyObject(datetime.now))` is
 really just a slightly deferred `datetime.now()` as the first time the
 wrapped function is evaluated the returned value is persisted. In other
 words `MinValueValidator(SimpleLazyObject(datetime.now))` is really
 equivalent to `MinValueValidator(datetime.now())` and is the same class of
 problems the `DateTimeField(default=datetime.now())` check was dealing
 with.

 Now what I think you were trying to do here is provide a validator that
 makes sure it's not possible to provide a past datetime value and this was
 not working appropriately as the validator was actually performing a check
 against the first time the `SimpleLazyObject` wrapped function was
 resolved. For this purpose you should be using `django.utils.lazy`
 instead.

 {{{#!python
 In [1]: from datetime import datetime, timedelta

 In [2]: from django.utils.functional import lazy

 In [3]: lazy_now = lazy(datetime.now, datetime)()

 In [4]: from django.core.validators import MinValueValidator

 In [5]: validator = MinValueValidator(lazy_now)

 In [6]: lazy_now
 Out[6]: datetime.datetime(2018, 10, 16, 14, 38, 59, 125760)

 In [7]: type(lazy_now)
 Out[7]: django.utils.functional.__proxy__

 In [8]: validator(datetime.now())

 ValidationError: [u'Ensure this value is greater than or equal to
 2018-10-16 14:39:24.060516.']

 In [9]: validator(datetime.now() + timedelta(seconds=1))  # account for
 the function call duration
 }}}

 The only issue here is that `Promise` serialization assumes `lazy`
 
[https://github.com/django/django/blob/1c0bf95ff6f0dd32dd0424f0becbdfcf344d37be/django/db/migrations/serializer.py#L275-L276
 is always dealing with strings] because the main use of them internally is
 for translations through the `gettext` function #21008.

 In summary, this is a misuse of `SimpleLazyObject` and `lazy` should have
 been used instead but `Promise` serialization currently assumes it's only
 used for string values and it should be adjusted to deal with other types
 as well. What I suggest doing is inspecting wrapped types of the promise
 object and only perform a `str` on the value if the types are `(str,)`.
 Else the value should be deconstructed as a `django.utils.functional.lazy`
 import and a `lazy(...)()` reconstruction.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29852#comment:6>
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.018cd5879a87fc613373ee5aa38e3a5d%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to