Opened 6 years ago

Last modified 6 years ago

#25426 closed Bug

pickling SimpleLazyObject fails just after accessing related object of wrapped model instance. — at Version 2

Reported by: Iru Hwang Owned by: nobody
Component: Core (Serialization) Version: 1.8
Severity: Release blocker Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Iru Hwang)

Reproduction step :

import pickle
import django

from django.contrib.auth.models import User
from django.utils.functional import SimpleLazyObject

u = User.objects.select_related('profile').get(id=153)
o = SimpleLazyObject(lambda :u)
pickle.dumps(o) # FAILS
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/local/lib/python2.7/", line 224, in dump
  File "/usr/local/lib/python2.7/", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/local/lib/python2.7/", line 419, in save_reduce
  File "/usr/local/lib/python2.7/", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/local/lib/python2.7/", line 649, in save_dict
  File "/usr/local/lib/python2.7/", line 661, in _batch_setitems
    for k, v in items:
RuntimeError: dictionary changed size during iteration
pickle.dumps(o) # SUCCEEDS 

This also fails :

u = User.objects.get(id=153)
o = SimpleLazyObject(lambda :u)

This looks to be related with change in Model.reduce in django 1.8

    def __reduce__(self):
        Provides pickling support. Normally, this just dispatches to Python's                                                                                                                                                                                                   
        standard handling. However, for models with deferred field loading, we                                                                                                                                                                                                  
        need to do things manually, as they're dynamically created classes and                                                                                                                                                                                                  
        only module-level classes can be pickled by the default path.                                                                                                                                                                                                           
        data = self.__dict__
        data[DJANGO_VERSION_PICKLE_KEY] = get_version()
        if not self._deferred:
            class_id = self._meta.app_label, self._meta.object_name
            return model_unpickle, (class_id, [], simple_class_factory), data
        defers = []
        for field in self._meta.fields:
            if isinstance(self.__class__.__dict__.get(field.attname),
        model = self._meta.proxy_for_model
        class_id = model._meta.app_label, model._meta.object_name
        return (model_unpickle, (class_id, defers, deferred_class_factory), data)

Change History (2)

comment:1 Changed 6 years ago by Iru Hwang

Description: modified (diff)

comment:2 Changed 6 years ago by Iru Hwang

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top