First test with moving Attributes to m2m field. Not working yet.
This commit is contained in:
parent
a0a94df83d
commit
a1d818f8aa
7 changed files with 169 additions and 143 deletions
|
|
@ -17,13 +17,13 @@ transparently through the decorating TypeClass.
|
||||||
import traceback
|
import traceback
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models.signals import post_init, pre_delete
|
from django.db.models.signals import m2m_changed
|
||||||
|
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
|
||||||
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||||
from src.server.caches import attr_post_init, attr_pre_delete
|
|
||||||
from src.typeclasses.typeclass import TypeClass
|
from src.typeclasses.typeclass import TypeClass
|
||||||
from src.players.models import PlayerNick
|
from src.players.models import PlayerNick
|
||||||
from src.objects.manager import ObjectManager
|
from src.objects.manager import ObjectManager
|
||||||
|
|
@ -36,7 +36,7 @@ from src.utils.utils import make_iter, to_unicode, variable_from_module, inherit
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
#__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB")
|
#__all__ = ("Alias", "ObjectNick", "ObjectDB")
|
||||||
|
|
||||||
_ScriptDB = None
|
_ScriptDB = None
|
||||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||||
|
|
@ -66,8 +66,8 @@ class ObjAttribute(Attribute):
|
||||||
verbose_name_plural = "Object Attributes"
|
verbose_name_plural = "Object Attributes"
|
||||||
|
|
||||||
# attach the cache handlers
|
# attach the cache handlers
|
||||||
post_init.connect(attr_post_init, sender=ObjAttribute, dispatch_uid="objattrcache")
|
#post_init.connect(attr_post_init, sender=ObjAttribute, dispatch_uid="objattrcache")
|
||||||
pre_delete.connect(attr_pre_delete, sender=ObjAttribute, dispatch_uid="objattrcache")
|
#pre_delete.connect(attr_pre_delete, sender=ObjAttribute, dispatch_uid="objattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -518,7 +518,7 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading.
|
# this is required to properly handle attributes and typeclass loading.
|
||||||
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
||||||
_attribute_class = ObjAttribute
|
#_attribute_class = ObjAttribute
|
||||||
_db_model_name = "objectdb" # used by attributes to safely store objects
|
_db_model_name = "objectdb" # used by attributes to safely store objects
|
||||||
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ from django.db.models.signals import post_init, pre_delete
|
||||||
|
|
||||||
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||||
from src.server.caches import attr_post_init, attr_pre_delete
|
|
||||||
|
|
||||||
from src.players import manager
|
from src.players import manager
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
|
|
@ -44,7 +43,7 @@ from src.utils.utils import inherits_from, make_iter
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
__all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB")
|
__all__ = ("PlayerNick", "PlayerDB")
|
||||||
|
|
||||||
_ME = _("me")
|
_ME = _("me")
|
||||||
_SELF = _("self")
|
_SELF = _("self")
|
||||||
|
|
@ -77,8 +76,8 @@ class PlayerAttribute(Attribute):
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Player Attribute"
|
verbose_name = "Player Attribute"
|
||||||
|
|
||||||
post_init.connect(attr_post_init, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
#post_init.connect(attr_post_init, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
||||||
pre_delete.connect(attr_pre_delete, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
#pre_delete.connect(attr_pre_delete, sender=PlayerAttribute, dispatch_uid="playerattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -252,7 +251,7 @@ class PlayerDB(TypedObject):
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading
|
# this is required to properly handle attributes and typeclass loading
|
||||||
_typeclass_paths = settings.PLAYER_TYPECLASS_PATHS
|
_typeclass_paths = settings.PLAYER_TYPECLASS_PATHS
|
||||||
_attribute_class = PlayerAttribute
|
#_attribute_class = PlayerAttribute
|
||||||
_db_model_name = "playerdb" # used by attributes to safely store objects
|
_db_model_name = "playerdb" # used by attributes to safely store objects
|
||||||
_default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player"
|
_default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,11 @@ from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_init, pre_delete
|
from django.db.models.signals import post_init, pre_delete
|
||||||
|
|
||||||
from src.server.caches import attr_post_init, attr_pre_delete
|
|
||||||
from src.typeclasses.models import Attribute, TypedObject
|
from src.typeclasses.models import Attribute, TypedObject
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from src.scripts.manager import ScriptManager
|
from src.scripts.manager import ScriptManager
|
||||||
|
|
||||||
__all__ = ("ScriptAttribute", "ScriptDB")
|
__all__ = ("ScriptDB",)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -50,9 +49,9 @@ class ScriptAttribute(Attribute):
|
||||||
verbose_name = "Script Attribute"
|
verbose_name = "Script Attribute"
|
||||||
verbose_name_plural = "Script Attributes"
|
verbose_name_plural = "Script Attributes"
|
||||||
|
|
||||||
# attach cache handlers for attribute lookup
|
## attach cache handlers for attribute lookup
|
||||||
post_init.connect(attr_post_init, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
#post_init.connect(attr_post_init, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
||||||
pre_delete.connect(attr_pre_delete, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
#pre_delete.connect(attr_pre_delete, sender=ScriptAttribute, dispatch_uid="scriptattrcache")
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -254,7 +253,7 @@ class ScriptDB(TypedObject):
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading
|
# this is required to properly handle attributes and typeclass loading
|
||||||
_typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS
|
_typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS
|
||||||
_attribute_class = ScriptAttribute
|
#_attribute_class = ScriptAttribute
|
||||||
_db_model_name = "scriptdb" # used by attributes to safely store objects
|
_db_model_name = "scriptdb" # used by attributes to safely store objects
|
||||||
_default_typeclass_path = settings.BASE_SCRIPT_TYPECLASS or "src.scripts.scripts.DoNothing"
|
_default_typeclass_path = settings.BASE_SCRIPT_TYPECLASS or "src.scripts.scripts.DoNothing"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,26 +141,34 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg
|
||||||
# to any property).
|
# to any property).
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
# connected to post_init signal (connected in respective Attribute model)
|
# connected to m2m_changed signal in respective model class
|
||||||
def attr_post_init(sender, instance=None, **kwargs):
|
def update_attr_cache(sender, **kwargs):
|
||||||
"Called when attribute is created or retrieved in connection with obj."
|
"Called when the many2many relation changes some way"
|
||||||
#print "attr_post_init:", instance, instance.db_obj, instance.db_key
|
obj = kwargs['instance']
|
||||||
hid = hashid(_GA(instance, "db_obj"), "-%s" % _GA(instance, "db_key"))
|
model = kwargs['model']
|
||||||
if hid:
|
action = kwargs['action']
|
||||||
global _ATTR_CACHE
|
if kwargs['reverse']:
|
||||||
_ATTR_CACHE[hid] = sender
|
# the reverse relation changed (the Attribute itself was acted on)
|
||||||
#_ATTR_CACHE.set(hid, sender)
|
pass
|
||||||
|
else:
|
||||||
# connected to pre_delete signal (connected in respective Attribute model)
|
# forward relation changed (the Object holding the Attribute m2m field)
|
||||||
def attr_pre_delete(sender, instance=None, **kwargs):
|
if action == "post_add":
|
||||||
"Called when attribute is deleted (del_attribute)"
|
# cache all added objects
|
||||||
#print "attr_pre_delete:", instance, instance.db_obj, instance.db_key
|
for attr_id in kwargs["pk_set"]:
|
||||||
hid = hashid(_GA(instance, "db_obj"), "-%s" % _GA(instance, "db_key"))
|
attr_obj = model.objects.get(pk=attr_id)
|
||||||
if hid:
|
set_attr_cache(obj, _GA(attr_obj, "db_key"), attr_obj)
|
||||||
#print "attr_pre_delete:", _GA(instance, "db_key")
|
elif action == "post_remove":
|
||||||
global _ATTR_CACHE
|
# obj.db_attributes.remove(attr) was called
|
||||||
del _ATTR_CACHE[hid]
|
for attr_id in kwargs["pk_set"]:
|
||||||
#_ATTR_CACHE.delete(hid)
|
attr_obj = model.objects.get(pk=attr_id)
|
||||||
|
del_attr_cache(obj, _GA(attr_obj, "db_key"))
|
||||||
|
attr_obj.delete()
|
||||||
|
elif action == "post_clear":
|
||||||
|
# obj.db_attributes.clear() was called
|
||||||
|
for attr_id in kwargs["pk_set"]:
|
||||||
|
attr_obj = model.objects.get(pk=attr_id)
|
||||||
|
del_attr_cache(obj, _GA(attr_obj, "db_key"))
|
||||||
|
attr_obj.delete()
|
||||||
|
|
||||||
# access methods
|
# access methods
|
||||||
|
|
||||||
|
|
@ -169,15 +177,23 @@ def get_attr_cache(obj, attrname):
|
||||||
hid = hashid(obj, "-%s" % attrname)
|
hid = hashid(obj, "-%s" % attrname)
|
||||||
return hid and _ATTR_CACHE.get(hid, None) or None
|
return hid and _ATTR_CACHE.get(hid, None) or None
|
||||||
|
|
||||||
def set_attr_cache(attrobj):
|
def set_attr_cache(obj, attrname, attrobj):
|
||||||
"Set the attr cache manually; this can be used to update"
|
"Set the attr cache manually; this can be used to update"
|
||||||
attr_post_init(None, instance=attrobj)
|
global _ATTR_CACHE
|
||||||
|
hid = hashid(obj, "-%s" % attrname)
|
||||||
|
_ATTR_CACHE[hid] = attrobj
|
||||||
|
|
||||||
|
def del_attr_cache(obj, attrname):
|
||||||
|
"Del attribute cache"
|
||||||
|
global _ATTR_CACHE
|
||||||
|
hid = hashid(obj, "-%s" % attrname)
|
||||||
|
if hid in _ATTR_CACHE:
|
||||||
|
del _ATTR_CACHE[hid]
|
||||||
|
|
||||||
def flush_attr_cache():
|
def flush_attr_cache():
|
||||||
"Clear attribute cache"
|
"Clear attribute cache"
|
||||||
global _ATTR_CACHE
|
global _ATTR_CACHE
|
||||||
_ATTR_CACHE = {}
|
_ATTR_CACHE = {}
|
||||||
#_ATTR_CACHE.clear()
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Property cache - this is a generic cache for properties stored on models.
|
# Property cache - this is a generic cache for properties stored on models.
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ all Attributes and TypedObjects).
|
||||||
"""
|
"""
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from src.utils import idmapper
|
from src.utils import idmapper
|
||||||
from src.utils.utils import make_iter
|
from src.utils.utils import make_iter
|
||||||
from src.utils.dbserialize import to_pickle
|
from src.utils.dbserialize import to_pickle
|
||||||
|
|
||||||
__all__ = ("AttributeManager", "TypedObjectManager")
|
__all__ = ("AttributeManager", "TypedObjectManager")
|
||||||
|
_GA = object.__getattribute__
|
||||||
|
|
||||||
# Managers
|
# Managers
|
||||||
|
|
||||||
|
|
@ -50,26 +52,33 @@ class AttributeManager(models.Manager):
|
||||||
|
|
||||||
def attr_namesearch(self, searchstr, obj, exact_match=True):
|
def attr_namesearch(self, searchstr, obj, exact_match=True):
|
||||||
"""
|
"""
|
||||||
Searches the object's attributes for name matches.
|
Searches the object's attributes for attribute key matches.
|
||||||
|
|
||||||
searchstr: (str) A string to search for.
|
searchstr: (str) A string to search for.
|
||||||
"""
|
"""
|
||||||
# Retrieve the list of attributes for this object.
|
# Retrieve the list of attributes for this object.
|
||||||
|
|
||||||
if exact_match:
|
if exact_match:
|
||||||
return self.filter(db_obj=obj).filter(
|
return _GA("obj", "db_attributes").filter(db_key__iexact=searchstr)
|
||||||
db_key__iexact=searchstr)
|
|
||||||
else:
|
else:
|
||||||
return self.filter(db_obj=obj).filter(
|
return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr)
|
||||||
db_key__icontains=searchstr)
|
|
||||||
|
|
||||||
def attr_valuesearch(self, searchstr, obj=None):
|
def attr_valuesearch(self, searchstr, obj=None):
|
||||||
"""
|
"""
|
||||||
Searches for Attributes with a given value on obj
|
Searches obj for Attributes with a given value.
|
||||||
|
searchstr - value to search for. This may be any suitable object.
|
||||||
|
obj - limit to a given object instance
|
||||||
|
|
||||||
|
If no restraint is given, all Attributes on all types of objects
|
||||||
|
will be searched. It's highly recommended to at least
|
||||||
|
supply the objclass argument (DBObject, DBScript or DBPlayer)
|
||||||
|
to restrict this lookup.
|
||||||
"""
|
"""
|
||||||
if obj:
|
if obj:
|
||||||
return self.filter(db_obj=obj, db_value=searchstr)
|
return _GA(obj, "db_attributes").filter(db_value=searchstr)
|
||||||
return self.filter(db_value=searchstr)
|
return self.filter(db_value=searchstr)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# helper functions for the TypedObjectManager.
|
# helper functions for the TypedObjectManager.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,18 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
#from collections import defaultdict
|
#from collections import defaultdict
|
||||||
|
|
||||||
from django.db import models, IntegrityError
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models.fields import AutoField, FieldDoesNotExist
|
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
||||||
from src.server.caches import get_attr_cache, set_attr_cache
|
from src.server.caches import get_attr_cache, set_attr_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, flush_attr_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, flush_attr_cache
|
||||||
|
|
||||||
|
from django.db.models.signals import m2m_changed
|
||||||
|
from src.server.caches import update_attr_cache
|
||||||
|
|
||||||
#from src.server.caches import call_ndb_hooks
|
#from src.server.caches import call_ndb_hooks
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
from src.typeclasses import managers
|
from src.typeclasses import managers
|
||||||
|
|
@ -61,6 +64,7 @@ _GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_DA = object.__delattr__
|
_DA = object.__delattr__
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Attributes
|
# Attributes
|
||||||
|
|
@ -106,12 +110,12 @@ class Attribute(SharedMemoryModel):
|
||||||
|
|
||||||
db_key = models.CharField('key', max_length=255, db_index=True)
|
db_key = models.CharField('key', max_length=255, db_index=True)
|
||||||
# access through the value property
|
# access through the value property
|
||||||
db_value = PickledObjectField('value2', null=True)
|
db_value = PickledObjectField('value', null=True)
|
||||||
# Lock storage
|
# Lock storage
|
||||||
db_lock_storage = models.TextField('locks', blank=True)
|
db_lock_storage = models.TextField('locks', blank=True)
|
||||||
# references the object the attribute is linked to (this is set
|
# references the object the attribute is linked to (this is set
|
||||||
# by each child class to this abstract class)
|
# by each child class to this abstract class)
|
||||||
db_obj = None # models.ForeignKey("RefencedObject")
|
db_obj = None # models.ForeignKey("RefencedObject") #TODO-remove
|
||||||
# time stamp
|
# time stamp
|
||||||
db_date_created = models.DateTimeField('date_created', editable=False, auto_now_add=True)
|
db_date_created = models.DateTimeField('date_created', editable=False, auto_now_add=True)
|
||||||
|
|
||||||
|
|
@ -128,7 +132,7 @@ class Attribute(SharedMemoryModel):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
abstract = True
|
#abstract = True
|
||||||
verbose_name = "Evennia Attribute"
|
verbose_name = "Evennia Attribute"
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
|
|
@ -426,7 +430,8 @@ class TypedObject(SharedMemoryModel):
|
||||||
# Lock storage
|
# Lock storage
|
||||||
db_lock_storage = models.TextField('locks', blank=True, help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.")
|
db_lock_storage = models.TextField('locks', blank=True, help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.")
|
||||||
|
|
||||||
#db_attributes = models.ManyToManyField(Attribute, related_name="%(app_label)s_%(class)s_related")
|
# attribute store
|
||||||
|
db_attributes = models.ManyToManyField(Attribute, null=True, help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).')
|
||||||
|
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = managers.TypedObjectManager()
|
objects = managers.TypedObjectManager()
|
||||||
|
|
@ -923,10 +928,10 @@ class TypedObject(SharedMemoryModel):
|
||||||
#
|
#
|
||||||
|
|
||||||
#
|
#
|
||||||
# Fully persistent attributes. You usually access these
|
# Fully attr_obj attributes. You usually access these
|
||||||
# through the obj.db.attrname method.
|
# through the obj.db.attrname method.
|
||||||
|
|
||||||
# Helper methods for persistent attributes
|
# Helper methods for attr_obj attributes
|
||||||
|
|
||||||
def has_attribute(self, attribute_name):
|
def has_attribute(self, attribute_name):
|
||||||
"""
|
"""
|
||||||
|
|
@ -935,10 +940,9 @@ class TypedObject(SharedMemoryModel):
|
||||||
attribute_name: (str) The attribute's name.
|
attribute_name: (str) The attribute's name.
|
||||||
"""
|
"""
|
||||||
if not get_attr_cache(self, attribute_name):
|
if not get_attr_cache(self, attribute_name):
|
||||||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
attr_obj = _GA(self, "db_attributes").filter(db_key__iexact=attribute_name)
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
if attr_obj:
|
||||||
if attrib_obj:
|
set_attr_cache(self, attribute_name, attr_obj[0])
|
||||||
set_attr_cache(attrib_obj[0])
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -956,46 +960,38 @@ class TypedObject(SharedMemoryModel):
|
||||||
below to perform access-checked modification of attributes. Lock
|
below to perform access-checked modification of attributes. Lock
|
||||||
types checked by secureattr are 'attrread','attredit','attrcreate'.
|
types checked by secureattr are 'attrread','attredit','attrcreate'.
|
||||||
"""
|
"""
|
||||||
attrib_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if not attrib_obj:
|
if not attr_obj:
|
||||||
attrclass = _GA(self, "_attribute_class")
|
# check if attribute already exists
|
||||||
# check if attribute already exists.
|
attr_obj = _GA(self, "db_attributes").filter(db_key__iexact=attribute_name)
|
||||||
attrib_obj = attrclass.objects.filter(
|
if attr_obj:
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
# re-use old attribute object
|
||||||
if attrib_obj:
|
attr_obj = attr_obj[0]
|
||||||
# use old attribute
|
set_attr_cache(self, attribute_name, attr_obj) # renew cache
|
||||||
attrib_obj = attrib_obj[0]
|
|
||||||
set_attr_cache(attrib_obj) # renew cache
|
|
||||||
else:
|
else:
|
||||||
# no match; create new attribute (this will cache automatically)
|
# no old attr available; create new (caches automatically)
|
||||||
attrib_obj = attrclass(db_key=attribute_name, db_obj=self)
|
attr_obj = Attribute(db_key=attribute_name)
|
||||||
|
attr_obj.save() # important
|
||||||
|
_GA(self, "db_attributes").add(attr_obj)
|
||||||
if lockstring:
|
if lockstring:
|
||||||
attrib_obj.locks.add(lockstring)
|
attr_obj.locks.add(lockstring)
|
||||||
# re-set an old attribute value
|
# we shouldn't need to fear stale objects, the signalling should catch all cases
|
||||||
try:
|
attr_obj.value = new_value
|
||||||
attrib_obj.value = new_value
|
|
||||||
except IntegrityError:
|
|
||||||
# this can happen if the cache was stale and the database object is
|
|
||||||
# missing. If so we need to clean self.hashid from the cache
|
|
||||||
flush_attr_cache(self)
|
|
||||||
self.delete()
|
|
||||||
raise IntegrityError("Attribute could not be saved - object %s was deleted from database." % self.key)
|
|
||||||
|
|
||||||
def get_attribute_obj(self, attribute_name, default=None):
|
def get_attribute_obj(self, attribute_name, default=None):
|
||||||
"""
|
"""
|
||||||
Get the actual attribute object named attribute_name
|
Get the actual attribute object named attribute_name
|
||||||
"""
|
"""
|
||||||
attrib_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if not attrib_obj:
|
if not attr_obj:
|
||||||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
attr_obj = _GA(self, "db_attributes").filter(db_key__iexact=attribute_name)
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
if not attr_obj:
|
||||||
if not attrib_obj:
|
|
||||||
return default
|
return default
|
||||||
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
attr_obj = attr_obj[0] # query evaluated here
|
||||||
return attrib_obj[0]
|
set_attr_cache(self, attribute_name, attr_obj)
|
||||||
return attrib_obj
|
return attr_obj
|
||||||
|
|
||||||
def get_attribute(self, attribute_name, default=None):
|
def get_attribute(self, attribute_name, default=None, raise_exception=False):
|
||||||
"""
|
"""
|
||||||
Returns the value of an attribute on an object. You may need to
|
Returns the value of an attribute on an object. You may need to
|
||||||
type cast the returned value from this function since the attribute
|
type cast the returned value from this function since the attribute
|
||||||
|
|
@ -1003,73 +999,76 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
attribute_name: (str) The attribute's name.
|
attribute_name: (str) The attribute's name.
|
||||||
default: What to return if no attribute is found
|
default: What to return if no attribute is found
|
||||||
|
raise_exception (bool) - raise an eception if no object exists instead of returning default.
|
||||||
"""
|
"""
|
||||||
attrib_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if not attrib_obj:
|
if not attr_obj:
|
||||||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
attr_obj = _GA(self, "db_atttributes").filter(db_key__iexact=attribute_name)
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
if not attr_obj:
|
||||||
if not attrib_obj:
|
if raise_exception:
|
||||||
|
raise AttributeError
|
||||||
return default
|
return default
|
||||||
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
attr_obj = attr_obj[0] # query is evaluated here
|
||||||
return attrib_obj[0].value
|
set_attr_cache(self, attribute_name, attr_obj)
|
||||||
return attrib_obj.value
|
return attr_obj.value
|
||||||
|
|
||||||
def get_attribute_raise(self, attribute_name):
|
# def get_attribute_raise(self, attribute_name):
|
||||||
"""
|
# """
|
||||||
Returns value of an attribute. Raises AttributeError
|
# Returns value of an attribute. Raises AttributeError
|
||||||
if no match is found.
|
# if no match is found.
|
||||||
|
#
|
||||||
|
# attribute_name: (str) The attribute's name.
|
||||||
|
# """
|
||||||
|
# attr_obj = get_attr_cache(self, attribute_name)
|
||||||
|
# if not attr_obj:
|
||||||
|
# attr_obj = _GA(self, "attributes").filter(db_key__iexact=attribute_name)
|
||||||
|
# if not attr_obj:
|
||||||
|
# raise AttributeError
|
||||||
|
# attr_obj = attrib_obj[0] # query is evaluated here
|
||||||
|
# set_attr_cache(self, attribute_name, attr_obj[0])
|
||||||
|
# return attr_obj.value
|
||||||
|
|
||||||
attribute_name: (str) The attribute's name.
|
def del_attribute(self, attribute_name, raise_exception=False):
|
||||||
"""
|
|
||||||
attrib_obj = get_attr_cache(self, attribute_name)
|
|
||||||
if not attrib_obj:
|
|
||||||
attrib_obj = _GA(self, "_attribute_class").objects.filter(
|
|
||||||
db_obj=self, db_key__iexact=attribute_name)
|
|
||||||
if not attrib_obj:
|
|
||||||
raise AttributeError
|
|
||||||
set_attr_cache(attrib_obj[0]) #query is first evaluated here
|
|
||||||
return attrib_obj[0].value
|
|
||||||
return attrib_obj.value
|
|
||||||
|
|
||||||
def del_attribute(self, attribute_name):
|
|
||||||
"""
|
"""
|
||||||
Removes an attribute entirely.
|
Removes an attribute entirely.
|
||||||
|
|
||||||
attribute_name: (str) The attribute's name.
|
attribute_name: (str) The attribute's name.
|
||||||
|
raise_exception (bool) - raise exception if attribute to delete
|
||||||
|
could not be found
|
||||||
"""
|
"""
|
||||||
attr_obj = get_attr_cache(self, attribute_name)
|
attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if attr_obj:
|
if attr_obj:
|
||||||
attr_obj.delete() # this will clear attr cache automatically
|
attr_obj.delete() # this will clear attr cache automatically
|
||||||
else:
|
else:
|
||||||
try:
|
attr_obj = _GA(self, "db_attributes").filter(db_key__iexact=attribute_name)
|
||||||
_GA(self, "_attribute_class").objects.filter(
|
if attr_obj:
|
||||||
db_obj=self, db_key__iexact=attribute_name)[0].delete()
|
attr_obj[0].delete()
|
||||||
except IndexError:
|
elif raise_exception:
|
||||||
pass
|
raise AttributeError
|
||||||
|
|
||||||
def del_attribute_raise(self, attribute_name):
|
# def del_attribute_raise(self, attribute_name):
|
||||||
"""
|
# """
|
||||||
Removes and attribute. Raises AttributeError if
|
# Removes and attribute. Raises AttributeError if
|
||||||
attribute is not found.
|
# attribute is not found.
|
||||||
|
#
|
||||||
attribute_name: (str) The attribute's name.
|
# attribute_name: (str) The attribute's name.
|
||||||
"""
|
# """
|
||||||
attr_obj = get_attr_cache(self, attribute_name)
|
# attr_obj = get_attr_cache(self, attribute_name)
|
||||||
if attr_obj:
|
# if attr_obj:
|
||||||
attr_obj.delete() # this will clear attr cache automatically
|
# attr_obj.delete() # this will clear attr cache automatically
|
||||||
else:
|
# else:
|
||||||
try:
|
# try:
|
||||||
_GA(self, "_attribute_class").objects.filter(
|
# _GA(self, "_attribute_class").objects.filter(
|
||||||
db_obj=self, db_key__iexact=attribute_name)[0].delete()
|
# db_obj=self, db_key__iexact=attribute_name)[0].delete()
|
||||||
except IndexError:
|
# except IndexError:
|
||||||
pass
|
# pass
|
||||||
raise AttributeError
|
# raise AttributeError
|
||||||
|
|
||||||
def get_all_attributes(self):
|
def get_all_attributes(self):
|
||||||
"""
|
"""
|
||||||
Returns all attributes defined on the object.
|
Returns all attributes defined on the object.
|
||||||
"""
|
"""
|
||||||
return list(_GA(self,"_attribute_class").objects.filter(db_obj=self))
|
return list(_GA(self, "db_attributes").all())
|
||||||
|
|
||||||
def attr(self, attribute_name=None, value=None, delete=False):
|
def attr(self, attribute_name=None, value=None, delete=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1195,12 +1194,12 @@ class TypedObject(SharedMemoryModel):
|
||||||
db = property(__db_get, __db_set, __db_del)
|
db = property(__db_get, __db_set, __db_del)
|
||||||
|
|
||||||
#
|
#
|
||||||
# NON-PERSISTENT storage methods
|
# NON-attr_obj storage methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def nattr(self, attribute_name=None, value=None, delete=False):
|
def nattr(self, attribute_name=None, value=None, delete=False):
|
||||||
"""
|
"""
|
||||||
This is the equivalence of self.attr but for non-persistent
|
This is the equivalence of self.attr but for non-attr_obj
|
||||||
stores. Will not raise error but return None.
|
stores. Will not raise error but return None.
|
||||||
"""
|
"""
|
||||||
if attribute_name == None:
|
if attribute_name == None:
|
||||||
|
|
@ -1226,7 +1225,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
#@property
|
#@property
|
||||||
def __ndb_get(self):
|
def __ndb_get(self):
|
||||||
"""
|
"""
|
||||||
A non-persistent store (ndb: NonDataBase). Everything stored
|
A non-attr_obj store (ndb: NonDataBase). Everything stored
|
||||||
to this is guaranteed to be cleared when a server is shutdown.
|
to this is guaranteed to be cleared when a server is shutdown.
|
||||||
Syntax is same as for the _get_db_holder() method and
|
Syntax is same as for the _get_db_holder() method and
|
||||||
property, e.g. obj.ndb.attr = value etc.
|
property, e.g. obj.ndb.attr = value etc.
|
||||||
|
|
@ -1235,7 +1234,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
return self._ndb_holder
|
return self._ndb_holder
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
class NdbHolder(object):
|
class NdbHolder(object):
|
||||||
"Holder for storing non-persistent attributes."
|
"Holder for storing non-attr_obj attributes."
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
return [val for val in self.__dict__.keys()
|
return [val for val in self.__dict__.keys()
|
||||||
if not val.startswith('_')]
|
if not val.startswith('_')]
|
||||||
|
|
@ -1313,3 +1312,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
as a new Typeclass instance.
|
as a new Typeclass instance.
|
||||||
"""
|
"""
|
||||||
self.__class__.flush_cached_instance(self)
|
self.__class__.flush_cached_instance(self)
|
||||||
|
|
||||||
|
|
||||||
|
# connect to signal
|
||||||
|
m2m_changed.connect(update_attr_cache, sender=TypedObject.db_attributes.through)
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ class TypeClass(object):
|
||||||
log_trace("This is probably due to an unsafe reload.")
|
log_trace("This is probably due to an unsafe reload.")
|
||||||
return # ignore delete
|
return # ignore delete
|
||||||
try:
|
try:
|
||||||
dbobj.del_attribute_raise(propname)
|
dbobj.del_attribute(propname, raise_exception=True)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s."
|
string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s."
|
||||||
raise AttributeError(string % (propname, dbobj,
|
raise AttributeError(string % (propname, dbobj,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue