#28188: Field._get_default() prevents model fields from being pickled
-------------------------------------+-------------------------------------
Reporter: Adam Alton | Owner: Adam
| Alton
Type: Bug | Status: assigned
Component: Database layer | Version: 1.11
(models, ORM) |
Severity: Release blocker | 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 Adam Alton):
* owner: nobody => Adam Alton
* status: new => assigned
Old description:
> The changes introduced in
> [[https://github.com/django/django/commit/d2a26c1a90e837777dabdf3d67ceec4d2a70fb86
> #diff-
> bf776a3b8e5dbfac2432015825ef8afe|d2a26c1a90e837777dabdf3d67ceec4d2a70fb86]]
> mean that in cases where a model field has a default and the default is
> not a callable, the field instance cannot be pickled.
>
> The offending line is:
>
> {{{#!python
> return lambda: self.default
> }}}
>
> This problem only exists in Django version >= 1.11.
>
> The reason for that ''lambda'' is because the code has been refactored so
> that all the logic for getting the default value has been moved into the
> ''_get_default'' method, which allows it to be cached with the
> ''@cached_property'' decorator. And then the ''get_default'' method
> expects the value returned from ''_get_default'' to always be a callable.
> That requirement for the value to always be a callable is the reason for
> wrapping the value with ''lambda'' in the case where it's not already a
> callable.
>
> My proposed solution is to simply move the ''if callable()'' check back
> out into the ''get_default'' method. This would mean that we lose a very
> minor bit of efficiency because we have to run that one ''if'' statement
> each time that ''get_default'' is called, but the rest of the logic would
> remain inside the cached ''_get_default'' method.
>
> I have made a commit here which contains my proposed fix and a test to
> prevent regression:
> https://github.com/adamalton/django/commit/a7c3f672d2ea45df2fdea8cad6ffcfa10172b133
New description:
The changes introduced in
[[https://github.com/django/django/commit/d2a26c1a90e837777dabdf3d67ceec4d2a70fb86
#diff-
bf776a3b8e5dbfac2432015825ef8afe|d2a26c1a90e837777dabdf3d67ceec4d2a70fb86]]
mean that in cases where a model field has a default and the default is
not a callable, the field instance cannot be pickled.
The offending line is:
{{{#!python
return lambda: self.default
}}}
This problem only exists in Django version >= 1.11.
The reason for that ''lambda'' is because the code has been refactored so
that all the logic for getting the default value has been moved into the
''_get_default'' method, which allows it to be cached with the
''@cached_property'' decorator. And then the ''get_default'' method
expects the value returned from ''_get_default'' to always be a callable.
That requirement for the value to always be a callable is the reason for
wrapping the value with ''lambda'' in the case where it's not already a
callable.
My proposed solution is to simply move the ''if callable()'' check back
out into the ''get_default'' method. This would mean that we lose a very
minor bit of efficiency because we have to run that one ''if'' statement
each time that ''get_default'' is called, but the rest of the logic would
remain inside the cached ''_get_default'' method.
I have made a commit here which contains my proposed fix and a test to
prevent regression:
https://github.com/adamalton/django/commit/ee6aafacfd01f2603943e8c4183805fd4a298355
--
--
Ticket URL: <https://code.djangoproject.com/ticket/28188#comment:2>
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/067.911a2f343cf932f9b8636178a57bf479%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.