jsonmodels
jsonmodels copied to clipboard
Field values are lost when pickling jsonmodel
I found out that using pickle serializer does not seem to work with jsonmodels. Using this simple model as an example (using Python 2.7.10):
class TestModel(models.Base):
a = fields.StringField(default="")
>>> m = TestModel(a="foo")
>>> m
TestModel(a='foo')
>>> pickle.loads(pickle.dumps(m))
TestModel(a='')
I'm using Python 2.7.10.
So the field value is lost when pickling. I found out that pickle uses __dict__
variable by default, and as this does not contain field data, it is not saved to the serialized form.
Using customized __getstate__()
and __setstate__()
methods I managed to make it work, I just had to make sure the _cache_key
variable is preserved, as unpickling the object does not run __init__()
and _cache_key
is not created:
class TestModel(models.Base):
a = fields.StringField(default="")
def __getstate__(self):
state = self.to_struct()
state['_cache_key'] = self._cache_key
return state
def __setstate__(self, data):
self._cache_key = data.pop('_cache_key')
self.populate(**data)
Does this seem to be a correct way to handle pickling? Also, is it necessary to pickle the _cache_key
, or would it be ok to just initialize it to a new value? (Some kind of _init_cache_key()
method would be a good addition in that case).
If this is a correct direction, I can make a pull request and test it with other Python versions. Support for JSON serializer would also be possible.
Thanks for the 'getstate()' and 'setstate()' information.
I was using jsonmodels with python multiprocessing Pool.map
and had problems with the copying of arguments, because of this very issue (pickle is used in the background).
I'm now using this hack (runtime patching of models.Base
):
def getstate(self):
state = self.to_struct()
state['_cache_key'] = self._cache_key
return state
models.Base.__getstate__ = getstate
def setstate(self, data):
self._cache_key = data.pop('_cache_key')
self.populate(**data)
models.Base.__setstate__ = setstate
PS: there's a similar issue with deepcopy
Hi,
I was able to omit this behavior by adding an inherited constructor:
class TestModel(models.Base):
a = fields.StringField(default="")
def __init__(self, *args, **kwargs):
super(TestModel, self).__init__(*args, **kwargs)
Afterwards, the error above disappeared.