Fixing so assigning to a nested attribute value doesn't loose data. It is unfortunately not possible to support direct assignment of deeply nested attributes at this time. So if obj.db.mydict = {1:{2:3}}, then obj.db.mydict[1] = 1 will work just fine, but obj.db.mydict[1][2] = 4 will not. For more advanced manipulation such as the latter case, store the dict in a temporary variable and save it back after changes have been done.

This commit is contained in:
Griatch 2011-09-20 19:59:08 +02:00
parent 058f8a9519
commit 18669c8700

View file

@ -62,7 +62,7 @@ class PackedDBobject(object):
""" """
Attribute helper class. Attribute helper class.
A container for storing and easily identifying database objects in A container for storing and easily identifying database objects in
the database (which doesn't suppport storing dbobjects directly). the database (which doesn't suppport storing db_objects directly).
""" """
def __init__(self, ID, db_model): def __init__(self, ID, db_model):
self.id = ID self.id = ID
@ -88,13 +88,15 @@ class PackedDict(dict):
self.db_store = False self.db_store = False
super(PackedDict, self).__init__(*args, **kwargs) super(PackedDict, self).__init__(*args, **kwargs)
def db_save(self): def db_save(self):
"save data to Attribute, if db_store is active" "save data to Attribute, if db_store is active"
if self.db_store: if self.db_store:
self.db_obj.value = self self.db_obj.value = self
def __setitem__(self, *args, **kwargs): def __setitem__(self, *args, **kwargs):
"Custom setitem that stores changed dict to database." "Custom setitem that stores changed dict to database."
super(PackedDict, self).__setitem__(*args, **kwargs) super(PackedDict, self).__setitem__(*args, **kwargs)
self.db_save() self.db_save()
def __getitem__(self, *args, **kwargs):
return super(PackedDict, self).__getitem__(*args, **kwargs)
def clear(self, *args, **kwargs): def clear(self, *args, **kwargs):
"Custom clear" "Custom clear"
super(PackedDict, self).clear(*args, **kwargs) super(PackedDict, self).clear(*args, **kwargs)
@ -217,6 +219,7 @@ class Attribute(SharedMemoryModel):
"Initializes the parent first -important!" "Initializes the parent first -important!"
SharedMemoryModel.__init__(self, *args, **kwargs) SharedMemoryModel.__init__(self, *args, **kwargs)
self.locks = LockHandler(self) self.locks = LockHandler(self)
self.value_cache = None
class Meta: class Meta:
"Define Django meta options" "Define Django meta options"
@ -285,15 +288,16 @@ class Attribute(SharedMemoryModel):
def value_get(self): def value_get(self):
""" """
Getter. Allows for value = self.value. Getter. Allows for value = self.value.
""" """
try: try:
return utils.to_unicode(self.validate_data(pickle.loads(utils.to_str(self.db_value)))) return utils.to_unicode(self.validate_data(pickle.loads(utils.to_str(self.db_value))))
except pickle.UnpicklingError: except pickle.UnpicklingError:
return self.db_value return self.db_value
#@value.setter #@value.setter
def value_set(self, new_value): def value_set(self, new_value):
"Setter. Allows for self.value = value" "Setter. Allows for self.value = value"
self.db_value = utils.to_unicode(pickle.dumps(utils.to_str(self.validate_data(new_value, setmode=True)))) self.db_value = utils.to_unicode(pickle.dumps(utils.to_str(self.validate_data(new_value, setmode=True))))
#self.db_value = utils.to_unicode(pickle.dumps(utils.to_str(self.validate_data(new_value))))
self.save() self.save()
#@value.deleter #@value.deleter
def value_del(self): def value_del(self):
@ -330,7 +334,7 @@ class Attribute(SharedMemoryModel):
def __unicode__(self): def __unicode__(self):
return u"%s(%s)" % (self.key, self.id) return u"%s(%s)" % (self.key, self.id)
def validate_data(self, item, setmode=False): def validate_data(self, item, niter=0, setmode=False):
""" """
We have to make sure to not store database objects raw, since We have to make sure to not store database objects raw, since
this will crash the system. Instead we must store their IDs this will crash the system. Instead we must store their IDs
@ -347,7 +351,7 @@ class Attribute(SharedMemoryModel):
sure that it's a normal built-in python object that is stored in the db, sure that it's a normal built-in python object that is stored in the db,
not the custom one. This will then just be updated later, assuring the not the custom one. This will then just be updated later, assuring the
pickling works as it should. pickling works as it should.
niter - iteration counter for recursive iterable search.
""" """
if isinstance(item, basestring): if isinstance(item, basestring):
# a string is unmodified # a string is unmodified
@ -371,27 +375,29 @@ class Attribute(SharedMemoryModel):
ret.append(self.validate_data(it)) ret.append(self.validate_data(it))
ret = tuple(ret) ret = tuple(ret)
elif type(item) == dict or type(item) == PackedDict: elif type(item) == dict or type(item) == PackedDict:
# handle dictionaries # handle dictionaries
if setmode: if setmode:
ret = {} ret = {}
for key, it in item.items(): for key, it in item.items():
ret[key] = self.validate_data(it, setmode=True) ret[key] = self.validate_data(it, niter=niter+1, setmode=True)
else: else:
ret = PackedDict(self) ret = PackedDict(self)
for key, it in item.items(): for key, it in item.items():
ret[key] = self.validate_data(it, setmode=True) ret[key] = self.validate_data(it, niter=niter+1)
ret.db_store = True if niter == 0:
ret.db_store = True
elif is_iter(item): elif is_iter(item):
# Note: ALL other iterables except dicts and tuples are stored&retrieved as lists! # Note: ALL other iterables except dicts and tuples are stored&retrieved as lists!
if setmode: if setmode:
ret = [] ret = []
for it in item: for it in item:
ret.append(self.validate_data(it, setmode=True)) ret.append(self.validate_data(it, niter=niter+1setmode=True))
else: else:
ret = PackedList(self) ret = PackedList(self)
for it in item: for it in item:
ret.append(self.validate_data(it)) ret.append(self.validate_data(it, niter=niter+1))
ret.db_store = True if niter == 0:
ret.db_store = True
elif has_parent('django.db.models.base.Model', item) or has_parent(PARENTS['typeclass'], item): elif has_parent('django.db.models.base.Model', item) or has_parent(PARENTS['typeclass'], item):
# db models must be stored as dbrefs # db models must be stored as dbrefs
db_model = [parent for parent, path in PARENTS.items() if has_parent(path, item)] db_model = [parent for parent, path in PARENTS.items() if has_parent(path, item)]