﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
25426	pickling SimpleLazyObject fails just after accessing related object of wrapped model instance.	Iru Hwang	nobody	"Reproduction step :
{{{
import pickle
import django
django.setup()

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/pickle.py"", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File ""/usr/local/lib/python2.7/pickle.py"", line 224, in dump
    self.save(obj)
  File ""/usr/local/lib/python2.7/pickle.py"", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File ""/usr/local/lib/python2.7/pickle.py"", line 419, in save_reduce
    save(state)
  File ""/usr/local/lib/python2.7/pickle.py"", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File ""/usr/local/lib/python2.7/pickle.py"", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File ""/usr/local/lib/python2.7/pickle.py"", 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)
o.profile
pickle.dumps(o)
}}}
This looks to be related with change in Model.__reduce__ in django 1.8
adding DJANGO_VERSION_PICKLE_KEY in __dict__

{{{
    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),
                          DeferredAttribute):
                defers.append(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)
}}}
"	Bug	new	Core (Serialization)	1.8	Normal				Unreviewed	0	0	0	0	0	0
