From 9e10a41e18992dfbf15b9a35715cf7a80485ff46 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 11 Jul 2013 15:59:03 +0200 Subject: [PATCH] First version with all caches seemingly working ok. Started to remove the on-model wrappers that are not handled by the idmapper metaclass. --- src/comms/models.py | 2 - src/help/models.py | 123 +++++++++++++++++----------------- src/objects/admin.py | 2 +- src/objects/models.py | 10 ++- src/players/models.py | 11 ++-- src/scripts/models.py | 9 ++- src/server/caches.py | 4 +- src/server/models.py | 3 - src/typeclasses/models.py | 131 +++++++++++++++++-------------------- src/utils/idmapper/base.py | 14 ++-- 10 files changed, 144 insertions(+), 165 deletions(-) diff --git a/src/comms/models.py b/src/comms/models.py index 187ddf4b99..2c039d7283 100644 --- a/src/comms/models.py +++ b/src/comms/models.py @@ -303,8 +303,6 @@ class Msg(SharedMemoryModel): logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) - _db_model_name = "msg" # used by attributes to safely store objects - # # Msg class methods # diff --git a/src/help/models.py b/src/help/models.py index e61474e67d..8890009004 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -45,7 +45,7 @@ class HelpEntry(SharedMemoryModel): # These database fields are all set using their corresponding properties, # named same as the field, but withtout the db_* prefix. - # title of the help + # title of the help entry db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for') # help category db_help_category = models.CharField("help category", max_length=255, default="General", @@ -72,9 +72,6 @@ class HelpEntry(SharedMemoryModel): verbose_name = "Help Entry" verbose_name_plural = "Help Entries" - # used by Attributes to safely retrieve stored object - _db_model_name = "helpentry" - # Wrapper properties to easily set database fields. These are # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() @@ -85,53 +82,53 @@ class HelpEntry(SharedMemoryModel): # key property (wraps db_key) #@property - def __key_get(self): - "Getter. Allows for value = self.key" - return self.db_key - #@key.setter - def __key_set(self, value): - "Setter. Allows for self.key = value" - self.db_key = value - self.save() - #@key.deleter - def __key_del(self): - "Deleter. Allows for del self.key. Deletes entry." - self.delete() - key = property(__key_get, __key_set, __key_del) + #def __key_get(self): + # "Getter. Allows for value = self.key" + # return self.db_key + ##@key.setter + #def __key_set(self, value): + # "Setter. Allows for self.key = value" + # self.db_key = value + # self.save() + ##@key.deleter + #def __key_del(self): + # "Deleter. Allows for del self.key. Deletes entry." + # self.delete() + #key = property(__key_get, __key_set, __key_del) - # help_category property (wraps db_help_category) - #@property - def __help_category_get(self): - "Getter. Allows for value = self.help_category" - return self.db_help_category - #@help_category.setter - def __help_category_set(self, value): - "Setter. Allows for self.help_category = value" - self.db_help_category = value - self.save() - #@help_category.deleter - def __help_category_del(self): - "Deleter. Allows for del self.help_category" - self.db_help_category = "General" - self.save() - help_category = property(__help_category_get, __help_category_set, __help_category_del) + ## help_category property (wraps db_help_category) + ##@property + #def __help_category_get(self): + # "Getter. Allows for value = self.help_category" + # return self.db_help_category + ##@help_category.setter + #def __help_category_set(self, value): + # "Setter. Allows for self.help_category = value" + # self.db_help_category = value + # self.save() + ##@help_category.deleter + #def __help_category_del(self): + # "Deleter. Allows for del self.help_category" + # self.db_help_category = "General" + # self.save() + #help_category = property(__help_category_get, __help_category_set, __help_category_del) - # entrytext property (wraps db_entrytext) - #@property - def __entrytext_get(self): - "Getter. Allows for value = self.entrytext" - return self.db_entrytext - #@entrytext.setter - def __entrytext_set(self, value): - "Setter. Allows for self.entrytext = value" - self.db_entrytext = value - self.save() - #@entrytext.deleter - def __entrytext_del(self): - "Deleter. Allows for del self.entrytext" - self.db_entrytext = "" - self.save() - entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) + ## entrytext property (wraps db_entrytext) + ##@property + #def __entrytext_get(self): + # "Getter. Allows for value = self.entrytext" + # return self.db_entrytext + ##@entrytext.setter + #def __entrytext_set(self, value): + # "Setter. Allows for self.entrytext = value" + # self.db_entrytext = value + # self.save() + ##@entrytext.deleter + #def __entrytext_del(self): + # "Deleter. Allows for del self.entrytext" + # self.db_entrytext = "" + # self.save() + #entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) # permissions property #@property @@ -153,20 +150,20 @@ class HelpEntry(SharedMemoryModel): permissions = property(__permissions_get, __permissions_set, __permissions_del) # lock_storage property (wraps db_lock_storage) - #@property - def __lock_storage_get(self): - "Getter. Allows for value = self.lock_storage" - return self.db_lock_storage - #@nick.setter - def __lock_storage_set(self, value): - """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - self.db_lock_storage = value - self.save() - #@nick.deleter - def __lock_storage_del(self): - "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) + ##@property + #def __lock_storage_get(self): + # "Getter. Allows for value = self.lock_storage" + # return self.db_lock_storage + ##@nick.setter + #def __lock_storage_set(self, value): + # """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" + # self.db_lock_storage = value + # self.save() + ##@nick.deleter + #def __lock_storage_del(self): + # "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" + # logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) + #lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) # diff --git a/src/objects/admin.py b/src/objects/admin.py index 2e7a9448de..70ee844cb2 100644 --- a/src/objects/admin.py +++ b/src/objects/admin.py @@ -6,7 +6,7 @@ from django import forms from django.conf import settings from django.contrib import admin -from src.typeclases.models import Attribute +from src.typeclasses.models import Attribute from src.objects.models import ObjectDB, ObjectNick, Alias from src.utils.utils import mod_import diff --git a/src/objects/models.py b/src/objects/models.py index 78f928fe21..bf7e565fcb 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -187,8 +187,11 @@ class ObjectDB(TypedObject): # Database manager objects = ObjectManager() - # Add the object-specific handlers + # caches for quick lookups of typeclass loading. + _typeclass_paths = settings.OBJECT_TYPECLASS_PATHS + _default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object" + # Add the object-specific handlers def __init__(self, *args, **kwargs): "Parent must be initialized first." TypedObject.__init__(self, *args, **kwargs) @@ -495,11 +498,6 @@ class ObjectDB(TypedObject): # ObjectDB class access methods/properties # - # this is required to properly handle attributes and typeclass loading. - _typeclass_paths = settings.OBJECT_TYPECLASS_PATHS - #_attribute_class = ObjAttribute - _db_model_name = "objectdb" # used by attributes to safely store objects - _default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object" #@property def __sessions_get(self): diff --git a/src/players/models.py b/src/players/models.py index be090531c8..7b7a3f04c9 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -137,11 +137,15 @@ class PlayerDB(TypedObject): db_is_connected = models.BooleanField(default=False, verbose_name="is_connected", help_text="If player is connected to game or not") # database storage of persistant cmdsets. db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, - help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.") + help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.") # Database manager objects = manager.PlayerManager() + # caches for quick lookups + _typeclass_paths = settings.PLAYER_TYPECLASS_PATHS + _default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player" + class Meta: app_label = 'players' verbose_name = 'Player' @@ -229,11 +233,6 @@ class PlayerDB(TypedObject): def __unicode__(self): return u"%s(player#%s)" % (_GA(self, "name"), _GA(self, "dbid")) - # this is required to properly handle attributes and typeclass loading - _typeclass_paths = settings.PLAYER_TYPECLASS_PATHS - #_attribute_class = PlayerAttribute - _db_model_name = "playerdb" # used by attributes to safely store objects - _default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player" # name property (wraps self.user.username) #@property diff --git a/src/scripts/models.py b/src/scripts/models.py index d275dd5fe1..cc29d96580 100644 --- a/src/scripts/models.py +++ b/src/scripts/models.py @@ -96,6 +96,10 @@ class ScriptDB(TypedObject): # Database manager objects = ScriptManager() + # caches for quick lookups + _typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS + _default_typeclass_path = settings.BASE_SCRIPT_TYPECLASS or "src.scripts.scripts.DoNothing" + class Meta: "Define Django meta options" verbose_name = "Script" @@ -233,11 +237,6 @@ class ScriptDB(TypedObject): # # - # this is required to properly handle attributes and typeclass loading - _typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS - #_attribute_class = ScriptAttribute - _db_model_name = "scriptdb" # used by attributes to safely store objects - _default_typeclass_path = settings.BASE_SCRIPT_TYPECLASS or "src.scripts.scripts.DoNothing" def at_typeclass_error(self): """ diff --git a/src/server/caches.py b/src/server/caches.py index e93cc5da67..b4d2707001 100644 --- a/src/server/caches.py +++ b/src/server/caches.py @@ -90,7 +90,7 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg """ if raw: return - print "field_pre_save:", instance, update_fields# if hasattr(instance, "db_key") else instance, update_fields + #print "field_pre_save:", instance, update_fields# if hasattr(instance, "db_key") else instance, update_fields if update_fields: # this is a list of strings at this point. We want field objects update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) @@ -166,7 +166,7 @@ def post_attr_update(sender, **kwargs): obj = kwargs['instance'] model = kwargs['model'] action = kwargs['action'] - print "update_attr_cache:", obj, model, action + #print "update_attr_cache:", obj, model, action if kwargs['reverse']: # the reverse relation changed (the Attribute itself was acted on) pass diff --git a/src/server/models.py b/src/server/models.py index 4bb11c1bf1..da4ae08016 100644 --- a/src/server/models.py +++ b/src/server/models.py @@ -48,9 +48,6 @@ class ServerConfig(SharedMemoryModel): objects = ServerConfigManager() - # used by Attributes eventually storing this safely - _db_model_name = "serverconfig" - # Wrapper properties to easily set database fields. These are # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 631506fd82..f224f5d4f2 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -401,29 +401,29 @@ class TypedObject(SharedMemoryModel): # TypedObject Database Model setup # # - # These databse fields are all set using their corresponding properties, - # named same as the field, but withtou the db_* prefix. + # These databse fields are all accessed and set using their corresponding properties, + # named same as the field, but without the db_* prefix (no separate save() call is needed) - # Main identifier of the object, for searching. Can also - # be referenced as 'name'. + # Main identifier of the object, for searching. Is accessed with self.key or self.name db_key = models.CharField('key', max_length=255, db_index=True) - # This is the python path to the type class this object is tied to - # (the type class is what defines what kind of Object this is) - db_typeclass_path = models.CharField('typeclass', max_length=255, null=True, help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.") - # Creation date + # This is the python path to the type class this object is tied to the type class is what defines what kind of Object this is) + db_typeclass_path = models.CharField('typeclass', max_length=255, null=True, + help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.") + # Creation date. This is not changed once the object is created. db_date_created = models.DateTimeField('creation date', editable=False, auto_now_add=True) # Permissions (access these through the 'permissions' property) - db_permissions = models.CharField('permissions', max_length=255, blank=True, help_text="a comma-separated list of text strings checked by certain locks. They are often used for hierarchies, such as letting a Player have permission 'Wizards', 'Builders' etc. Character objects use 'Players' by default. Most other objects don't have any permissions.") + db_permissions = models.CharField('permissions', max_length=255, blank=True, + help_text="a comma-separated list of text strings checked by in-game locks. They are often used for hierarchies, such as letting a Player have permission 'Wizards', 'Builders' etc. Character objects use 'Players' by default. Most other objects don't have any permissions.") # 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.") - - # 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).') - + 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.") + # attribute store. This is accessed through the self.db handler. + 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 objects = managers.TypedObjectManager() - # object cache and flags + # quick on-object typeclass cache for speed _cached_typeclass = None # lock handler self.locks @@ -441,6 +441,7 @@ class TypedObject(SharedMemoryModel): verbose_name = "Evennia Database Object" ordering = ['-db_date_created', 'id', 'db_typeclass_path', 'db_key'] + # wrapper # Wrapper properties to easily set database fields. These are # @property decorators that allows to access these fields using # normal python operations (without having to remember to save() @@ -449,7 +450,6 @@ class TypedObject(SharedMemoryModel): # value = self.attr and del self.attr respectively (where self # is the object in question). - # key property (wraps db_key) #@property #def __key_get(self): @@ -466,53 +466,44 @@ class TypedObject(SharedMemoryModel): # raise Exception("Cannot delete objectdb key!") #key = property(__key_get, __key_set, __key_del) - # name property (wraps db_key too - alias to self.key) - #@property - def __name_get(self): - "Getter. Allows for value = self.name" - return self.key - #@name.sette - def __name_set(self, value): - "Setter. Allows for self.name = value" - self.key = value - #@name.deleter - def __name_del(self): - "Deleter. Allows for del self.name" - raise Exception("Cannot delete name!") + # name property (alias to self.key) + def __name_get(self): return self.key + def __name_set(self, value): self.key = value + def __name_del(self): raise Exception("Cannot delete name") name = property(__name_get, __name_set, __name_del) - # typeclass_path property - we don't cache this. + # typeclass_path property - we manage this separately. #@property - def __typeclass_path_get(self): - "Getter. Allows for value = self.typeclass_path" - return _GA(self, "db_typeclass_path")#get_field_cache(self, "typeclass_path") - #@typeclass_path.setter - def __typeclass_path_set(self, value): - "Setter. Allows for self.typeclass_path = value" - _SA(self, "db_typeclass_path", value) - update_fields = ["db_typeclass_path"] if _GA(self, "_get_pk_val")(_GA(self, "_meta")) is not None else None - _GA(self, "save")(update_fields=update_fields) - #@typeclass_path.deleter - def __typeclass_path_del(self): - "Deleter. Allows for del self.typeclass_path" - self.db_typeclass_path = "" - _GA(self, "save")(update_fields=["db_typeclass_path"]) - typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del) + #def __typeclass_path_get(self): + # "Getter. Allows for value = self.typeclass_path" + # return _GA(self, "db_typeclass_path") + ##@typeclass_path.setter + #def __typeclass_path_set(self, value): + # "Setter. Allows for self.typeclass_path = value" + # _SA(self, "db_typeclass_path", value) + # update_fields = ["db_typeclass_path"] if _GA(self, "_get_pk_val")(_GA(self, "_meta")) is not None else None + # _GA(self, "save")(update_fields=update_fields) + ##@typeclass_path.deleter + #def __typeclass_path_del(self): + # "Deleter. Allows for del self.typeclass_path" + # self.db_typeclass_path = "" + # _GA(self, "save")(update_fields=["db_typeclass_path"]) + #typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del) # date_created property #@property - def __date_created_get(self): - "Getter. Allows for value = self.date_created" - return get_field_cache(self, "date_created") - #@date_created.setter - def __date_created_set(self, value): - "Setter. Allows for self.date_created = value" - raise Exception("Cannot change date_created!") - #@date_created.deleter - def __date_created_del(self): - "Deleter. Allows for del self.date_created" - raise Exception("Cannot delete date_created!") - date_created = property(__date_created_get, __date_created_set, __date_created_del) + #def __date_created_get(self): + # "Getter. Allows for value = self.date_created" + # return get_field_cache(self, "date_created") + ##@date_created.setter + #def __date_created_set(self, value): + # "Setter. Allows for self.date_created = value" + # raise Exception("Cannot change date_created!") + ##@date_created.deleter + #def __date_created_del(self): + # "Deleter. Allows for del self.date_created" + # raise Exception("Cannot delete date_created!") + #date_created = property(__date_created_get, __date_created_set, __date_created_del) # permissions property #@property @@ -537,18 +528,18 @@ class TypedObject(SharedMemoryModel): # lock_storage property (wraps db_lock_storage) #@property - def __lock_storage_get(self): - "Getter. Allows for value = self.lock_storage" - return get_field_cache(self, "lock_storage") - #@lock_storage.setter - def __lock_storage_set(self, value): - """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - set_field_cache(self, "lock_storage", value) - #@lock_storage.deleter - def __lock_storage_del(self): - "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) + #def __lock_storage_get(self): + # "Getter. Allows for value = self.lock_storage" + # return get_field_cache(self, "lock_storage") + ##@lock_storage.setter + #def __lock_storage_set(self, value): + # """Saves the lock_storage. This is usually not called directly, but through self.lock()""" + # set_field_cache(self, "lock_storage", value) + ##@lock_storage.deleter + #def __lock_storage_del(self): + # "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" + # logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) + #lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) @@ -560,8 +551,6 @@ class TypedObject(SharedMemoryModel): # these are identifiers for fast Attribute access and caching _typeclass_paths = settings.OBJECT_TYPECLASS_PATHS - _attribute_class = Attribute # replaced by relevant attribute class for child - _db_model_name = "typeclass" # used by attributes to safely store objects def __eq__(self, other): return other and hasattr(other, 'dbid') and self.dbid == other.dbid diff --git a/src/utils/idmapper/base.py b/src/utils/idmapper/base.py index 2b4e6ce460..10510e195f 100755 --- a/src/utils/idmapper/base.py +++ b/src/utils/idmapper/base.py @@ -87,18 +87,18 @@ class SharedMemoryModelBase(ModelBase): "Helper method to create property wrappers with unique names (must be in separate call)" def _get(cls, fname): "Wrapper for getting database field" - #print "_get wrapper:", fname, value, type(value) value = _GA(cls, fieldname) - if isinstance(value, (basestring, int, float, bool)): + if type(value) in (basestring, int, float, bool): return value elif hasattr(value, "typeclass"): return _GA(value, "typeclass") return value + def _set(cls, fname, value): "Wrapper for setting database field" if hasattr(value, "dbobj"): value = _GA(value, "dbobj") - else: + elif fname.isdigit() or fname.startswith("#"): # we also allow setting using dbrefs, if so we try to load the matching object. # (we assume the object is of the same type as the class holding the field, if # not a custom handler must be used for that field) @@ -107,25 +107,27 @@ class SharedMemoryModelBase(ModelBase): try: value = cls._default_manager.get(id=dbid) except ObjectDoesNotExist: - err = "Could not set %s. Tried to treat value '%s' as a dbref, but no matching object with that id was found." - err = err % (fname, value) - raise ObjectDoesNotExist(err) + # maybe it is just a name + pass #print "_set wrapper:", fname, value, type(value), cls._get_pk_val(cls._meta) _SA(cls, fname, value) # only use explicit update_fields in save if we actually have a # primary key assigned already (won't be when first creating object) update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None _GA(cls, "save")(update_fields=update_fields) + def _del(cls, fname): "Wrapper for clearing database field - sets it to None" _SA(cls, fname, None) update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None _GA(cls, "save")(update_fields=update_fields) + # create class wrappers fget = lambda cls: _get(cls, fieldname) fset = lambda cls, val: _set(cls, fieldname, val) fdel = lambda cls: _del(cls, fieldname) doc = "Wraps setting, saving and deleting the %s field." % fieldname + type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel, doc)) # exclude some models that should not auto-create wrapper fields