Migrate changes to include updating single elements of ListFields as well as MapFields by adding the same changes to BaseList. This is done by ensuring all BaseDicts and BaseLists have the correct name from the base of the nearest (Embedded)Document, then marking changes with their key or index when they are changed. Tests also all fixed up.
180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
import weakref
|
|
from mongoengine.common import _import_class
|
|
|
|
__all__ = ("BaseDict", "BaseList")
|
|
|
|
|
|
class BaseDict(dict):
|
|
"""A special dict so we can watch any changes"""
|
|
|
|
_dereferenced = False
|
|
_instance = None
|
|
_name = None
|
|
|
|
def __init__(self, dict_items, instance, name):
|
|
Document = _import_class('Document')
|
|
EmbeddedDocument = _import_class('EmbeddedDocument')
|
|
|
|
if isinstance(instance, (Document, EmbeddedDocument)):
|
|
self._instance = weakref.proxy(instance)
|
|
self._name = name
|
|
return super(BaseDict, self).__init__(dict_items)
|
|
|
|
def __getitem__(self, key, *args, **kwargs):
|
|
value = super(BaseDict, self).__getitem__(key)
|
|
|
|
EmbeddedDocument = _import_class('EmbeddedDocument')
|
|
if isinstance(value, EmbeddedDocument) and value._instance is None:
|
|
value._instance = self._instance
|
|
elif not isinstance(value, BaseDict) and isinstance(value, dict):
|
|
value = BaseDict(value, None, '%s.%s' % (self._name, key))
|
|
super(BaseDict, self).__setitem__(key, value)
|
|
value._instance = self._instance
|
|
elif not isinstance(value, BaseList) and isinstance(value, list):
|
|
value = BaseList(value, None, '%s.%s' % (self._name, key))
|
|
super(BaseDict, self).__setitem__(key, value)
|
|
value._instance = self._instance
|
|
return value
|
|
|
|
def __setitem__(self, key, value, *args, **kwargs):
|
|
self._mark_as_changed(key)
|
|
return super(BaseDict, self).__setitem__(key, value)
|
|
|
|
def __delete__(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseDict, self).__delete__(*args, **kwargs)
|
|
|
|
def __delitem__(self, key, *args, **kwargs):
|
|
self._mark_as_changed(key)
|
|
return super(BaseDict, self).__delitem__(key)
|
|
|
|
def __delattr__(self, key, *args, **kwargs):
|
|
self._mark_as_changed(key)
|
|
return super(BaseDict, self).__delattr__(key)
|
|
|
|
def __getstate__(self):
|
|
self.instance = None
|
|
self._dereferenced = False
|
|
return self
|
|
|
|
def __setstate__(self, state):
|
|
self = state
|
|
return self
|
|
|
|
def clear(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseDict, self).clear(*args, **kwargs)
|
|
|
|
def pop(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseDict, self).pop(*args, **kwargs)
|
|
|
|
def popitem(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseDict, self).popitem(*args, **kwargs)
|
|
|
|
def update(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseDict, self).update(*args, **kwargs)
|
|
|
|
def _mark_as_changed(self, key=None):
|
|
if hasattr(self._instance, '_mark_as_changed'):
|
|
if key:
|
|
self._instance._mark_as_changed('%s.%s' % (self._name, key))
|
|
else:
|
|
self._instance._mark_as_changed(self._name)
|
|
|
|
|
|
class BaseList(list):
|
|
"""A special list so we can watch any changes
|
|
"""
|
|
|
|
_dereferenced = False
|
|
_instance = None
|
|
_name = None
|
|
|
|
def __init__(self, list_items, instance, name):
|
|
Document = _import_class('Document')
|
|
EmbeddedDocument = _import_class('EmbeddedDocument')
|
|
|
|
if isinstance(instance, (Document, EmbeddedDocument)):
|
|
self._instance = weakref.proxy(instance)
|
|
self._name = name
|
|
return super(BaseList, self).__init__(list_items)
|
|
|
|
def __getitem__(self, key, *args, **kwargs):
|
|
value = super(BaseList, self).__getitem__(key)
|
|
|
|
EmbeddedDocument = _import_class('EmbeddedDocument')
|
|
if isinstance(value, EmbeddedDocument) and value._instance is None:
|
|
value._instance = self._instance
|
|
elif not isinstance(value, BaseDict) and isinstance(value, dict):
|
|
value = BaseDict(value, None, '%s.%s' % (self._name, key))
|
|
super(BaseList, self).__setitem__(key, value)
|
|
value._instance = self._instance
|
|
elif not isinstance(value, BaseList) and isinstance(value, list):
|
|
value = BaseList(value, None, '%s.%s' % (self._name, key))
|
|
super(BaseList, self).__setitem__(key, value)
|
|
value._instance = self._instance
|
|
return value
|
|
|
|
def __setitem__(self, key, value, *args, **kwargs):
|
|
self._mark_as_changed(key)
|
|
return super(BaseList, self).__setitem__(key, value)
|
|
|
|
def __delitem__(self, key, *args, **kwargs):
|
|
self._mark_as_changed(key)
|
|
return super(BaseList, self).__delitem__(key)
|
|
|
|
def __setslice__(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).__setslice__(*args, **kwargs)
|
|
|
|
def __delslice__(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).__delslice__(*args, **kwargs)
|
|
|
|
def __getstate__(self):
|
|
self.instance = None
|
|
self._dereferenced = False
|
|
return self
|
|
|
|
def __setstate__(self, state):
|
|
self = state
|
|
return self
|
|
|
|
def append(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).append(*args, **kwargs)
|
|
|
|
def extend(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).extend(*args, **kwargs)
|
|
|
|
def insert(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).insert(*args, **kwargs)
|
|
|
|
def pop(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).pop(*args, **kwargs)
|
|
|
|
def remove(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).remove(*args, **kwargs)
|
|
|
|
def reverse(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).reverse(*args, **kwargs)
|
|
|
|
def sort(self, *args, **kwargs):
|
|
self._mark_as_changed()
|
|
return super(BaseList, self).sort(*args, **kwargs)
|
|
|
|
def _mark_as_changed(self, key=None):
|
|
if hasattr(self._instance, '_mark_as_changed'):
|
|
if key:
|
|
self._instance._mark_as_changed('%s.%s' % (self._name, key))
|
|
else:
|
|
self._instance._mark_as_changed(self._name)
|