Fixed an issue with setting location. Still errors with creating new objects.
This commit is contained in:
parent
965e236d9a
commit
7351aacba5
5 changed files with 64 additions and 37 deletions
|
|
@ -178,9 +178,10 @@ class ObjectDB(TypedObject):
|
||||||
# db_key (also 'name' works), db_typeclass_path, db_date_created,
|
# db_key (also 'name' works), db_typeclass_path, db_date_created,
|
||||||
# db_permissions
|
# db_permissions
|
||||||
#
|
#
|
||||||
# These databse fields (including the inherited ones) are all set
|
# These databse fields (including the inherited ones) should normally be set
|
||||||
# using their corresponding properties, named same as the field,
|
# using their corresponding wrapper properties, named same as the field, but without
|
||||||
# but withtout the db_* prefix.
|
# the db_* prefix (e.g. the db_key field is set with self.key instead). The wrappers
|
||||||
|
# will automatically save and cache the data more efficiently.
|
||||||
|
|
||||||
# If this is a character object, the player is connected here.
|
# If this is a character object, the player is connected here.
|
||||||
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
|
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
|
||||||
|
|
@ -217,7 +218,6 @@ class ObjectDB(TypedObject):
|
||||||
_GA(self, "cmdset").update(init_mode=True)
|
_GA(self, "cmdset").update(init_mode=True)
|
||||||
_SA(self, "scripts", ScriptHandler(self))
|
_SA(self, "scripts", ScriptHandler(self))
|
||||||
_SA(self, "nicks", ObjectNickHandler(self))
|
_SA(self, "nicks", ObjectNickHandler(self))
|
||||||
# store the attribute class
|
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
# @property decorators that allows to access these fields using
|
# @property decorators that allows to access these fields using
|
||||||
|
|
@ -306,16 +306,17 @@ class ObjectDB(TypedObject):
|
||||||
del_field_cache(self, "sessid")
|
del_field_cache(self, "sessid")
|
||||||
sessid = property(__sessid_get, __sessid_set, __sessid_del)
|
sessid = property(__sessid_get, __sessid_set, __sessid_del)
|
||||||
|
|
||||||
def _db_location_handler(self, new_value, old_value=None):
|
def _db_location_handler(self, loc, old_value=None):
|
||||||
"This handles changes to the db_location field."
|
"This handles changes to the db_location field."
|
||||||
print "db_location_handler:", new_value, old_value
|
#print "db_location_handler:", loc, old_value
|
||||||
try:
|
try:
|
||||||
old_loc = old_value
|
old_loc = old_value
|
||||||
# new_value can be dbref, typeclass or dbmodel
|
# new_value can be dbref, typeclass or dbmodel
|
||||||
if ObjectDB.objects.dbref(new_value, reqhash=False):
|
if ObjectDB.objects.dbref(loc, reqhash=False):
|
||||||
loc = ObjectDB.objects.dbref_search(new_value)
|
loc = ObjectDB.objects.dbref_search(loc)
|
||||||
# this should not fail if new_value is valid.
|
if loc and type(loc) != ObjectDB:
|
||||||
loc = _GA(loc, "dbobj")
|
# this should not fail if new_value is valid.
|
||||||
|
loc = _GA(loc, "dbobj")
|
||||||
|
|
||||||
# recursive location check
|
# recursive location check
|
||||||
def is_loc_loop(loc, depth=0):
|
def is_loc_loop(loc, depth=0):
|
||||||
|
|
@ -328,22 +329,22 @@ class ObjectDB(TypedObject):
|
||||||
try: is_loc_loop(loc)
|
try: is_loc_loop(loc)
|
||||||
except RuntimeWarning: pass
|
except RuntimeWarning: pass
|
||||||
|
|
||||||
# set the location
|
#print "db_location_handler2:", _GA(loc, "db_key") if loc else loc, type(loc)
|
||||||
_SA(self, "db_location", loc)
|
|
||||||
# update the contents of each location
|
# update the contents of each location
|
||||||
if old_loc:
|
if old_loc:
|
||||||
_GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True)
|
_GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True)
|
||||||
if loc:
|
if loc:
|
||||||
_GA(loc, "contents_update")(self)
|
_GA(loc, "contents_update")(self)
|
||||||
|
return loc
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
string = "Cannot set location, "
|
string = "Cannot set location, "
|
||||||
string += "%s.location = %s would create a location-loop." % (self.key, new_value)
|
string += "%s.location = %s would create a location-loop." % (self.key, loc)
|
||||||
_GA(self, "msg")(_(string))
|
_GA(self, "msg")(_(string))
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise RuntimeError(string)
|
raise RuntimeError(string)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
string = "Cannot set location (%s): " % str(e)
|
string = "Cannot set location (%s): " % str(e)
|
||||||
string += "%s is not a valid location." % new_value
|
string += "%s is not a valid location." % loc
|
||||||
_GA(self, "msg")(_(string))
|
_GA(self, "msg")(_(string))
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise Exception(string)
|
raise Exception(string)
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg
|
||||||
"""
|
"""
|
||||||
if raw:
|
if raw:
|
||||||
return
|
return
|
||||||
|
print "field_pre_save:", _GA(instance, "db_key") if hasattr(instance, "db_key") else instance, update_fields
|
||||||
if update_fields:
|
if update_fields:
|
||||||
# this is a list of strings at this point. We want field objects
|
# this is a list of strings at this point. We want field objects
|
||||||
update_fields = (instance._meta.get_field_by_name(field)[0] for field in update_fields)
|
update_fields = (instance._meta.get_field_by_name(field)[0] for field in update_fields)
|
||||||
|
|
|
||||||
|
|
@ -436,7 +436,8 @@ class TypedObject(SharedMemoryModel):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"We must initialize the parent first - important!"
|
"We must initialize the parent first - important!"
|
||||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||||
self.locks = LockHandler(self)
|
_SA(self, "dbobj", self) # this allows for self-reference
|
||||||
|
_SA(self, "locks", LockHandler(self))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,10 @@ import os, threading
|
||||||
#from twisted.internet import reactor
|
#from twisted.internet import reactor
|
||||||
#from twisted.internet.threads import blockingCallFromThread
|
#from twisted.internet.threads import blockingCallFromThread
|
||||||
from twisted.internet.reactor import callFromThread
|
from twisted.internet.reactor import callFromThread
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models.base import Model, ModelBase
|
from django.db.models.base import Model, ModelBase
|
||||||
from django.db.models.signals import post_save, pre_delete, post_syncdb
|
from django.db.models.signals import post_save, pre_delete, post_syncdb
|
||||||
|
from src.utils.utils import dbref
|
||||||
|
|
||||||
from manager import SharedMemoryManager
|
from manager import SharedMemoryManager
|
||||||
|
|
||||||
|
|
@ -92,6 +94,7 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
|
|
||||||
def __init__(cls, *args, **kwargs):
|
def __init__(cls, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
Field shortcut creation:
|
||||||
Takes field names db_* and creates property wrappers named without the db_ prefix. So db_key -> key
|
Takes field names db_* and creates property wrappers named without the db_ prefix. So db_key -> key
|
||||||
This wrapper happens on the class level, so there is no overhead when creating objects. If a class
|
This wrapper happens on the class level, so there is no overhead when creating objects. If a class
|
||||||
already has a wrapper of the given name, the automatic creation is skipped. Note: Remember to
|
already has a wrapper of the given name, the automatic creation is skipped. Note: Remember to
|
||||||
|
|
@ -101,24 +104,47 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
def create_wrapper(cls, fieldname, wrappername):
|
def create_wrapper(cls, fieldname, wrappername):
|
||||||
"Helper method to create property wrappers with unique names (must be in separate call)"
|
"Helper method to create property wrappers with unique names (must be in separate call)"
|
||||||
def _get(cls, fname):
|
def _get(cls, fname):
|
||||||
return _GA(cls, fname)
|
"Wrapper for getting database field"
|
||||||
|
value = _GA(cls, fname)
|
||||||
|
if hasattr(value, "typeclass"):
|
||||||
|
return _GA(value, "typeclass")
|
||||||
|
#print "_get wrapper:", fname, value, type(value)
|
||||||
|
return value
|
||||||
def _set(cls, fname, value):
|
def _set(cls, fname, value):
|
||||||
|
"Wrapper for setting database field"
|
||||||
|
if hasattr(value, "dbobj"):
|
||||||
|
value = _GA(value, "dbobj")
|
||||||
|
else:
|
||||||
|
# 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)
|
||||||
|
dbid = dbref(value, reqhash=False)
|
||||||
|
if dbid:
|
||||||
|
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)
|
||||||
|
print "_set wrapper:", fname, value, type(value)
|
||||||
_SA(cls, fname, value)
|
_SA(cls, fname, value)
|
||||||
_GA(cls, "save")(update_fields=[fname]) # important!
|
_GA(cls, "save")(update_fields=[fname]) # important - this saves one field only
|
||||||
def _del(cls, fname):
|
def _del(cls, fname):
|
||||||
|
"Wrapper for clearing database field"
|
||||||
raise RuntimeError("You cannot delete field %s on %s; set it to None instead." % (fname, cls))
|
raise RuntimeError("You cannot delete field %s on %s; set it to None instead." % (fname, cls))
|
||||||
type(cls).__setattr__(cls, wrappername, property(lambda cls: _get(cls, fieldname),
|
type(cls).__setattr__(cls, wrappername, property(fget=lambda cls: _get(cls, fieldname),
|
||||||
lambda cls,val: _set(cls, fieldname, val),
|
fset=lambda cls,val: _set(cls, fieldname, val),
|
||||||
lambda cls: _del(cls, fieldname)))
|
fdel=lambda cls: _del(cls, fieldname),
|
||||||
# eclude some models that should not auto-create wrapper fields
|
doc="Wraps setting, saving and caching the %s field." % fieldname))
|
||||||
|
# exclude some models that should not auto-create wrapper fields
|
||||||
if cls.__name__ in ("ServerConfig", "TypeNick"):
|
if cls.__name__ in ("ServerConfig", "TypeNick"):
|
||||||
return
|
return
|
||||||
# dynamically create the properties
|
# dynamically create the wrapper properties for all fields not already handled
|
||||||
for field in cls._meta.fields:
|
for field in cls._meta.fields:
|
||||||
fieldname = field.name
|
fieldname = field.name
|
||||||
wrappername = fieldname == "id" and "dbid" or fieldname.replace("db_", "")
|
wrappername = fieldname == "id" and "dbid" or fieldname.replace("db_", "")
|
||||||
if not hasattr(cls, wrappername):
|
if not hasattr(cls, wrappername):
|
||||||
# make sure not to overload manually created wrappers on the model
|
# makes sure not to overload manually created wrappers on the model
|
||||||
#print "wrapping %s -> %s" % (fieldname, wrappername)
|
#print "wrapping %s -> %s" % (fieldname, wrappername)
|
||||||
create_wrapper(cls, fieldname, wrappername)
|
create_wrapper(cls, fieldname, wrappername)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -287,22 +287,20 @@ def pypath_to_realpath(python_path, file_ending='.py'):
|
||||||
|
|
||||||
def dbref(dbref, reqhash=True):
|
def dbref(dbref, reqhash=True):
|
||||||
"""
|
"""
|
||||||
Converts/checks if input is a valid dbref Valid forms of dbref
|
Converts/checks if input is a valid dbref.
|
||||||
(database reference number) are either a string '#N' or
|
If reqhash is set, only input strings on the form '#N', where N is an integer
|
||||||
an integer N. Output is the integer part.
|
is accepted. Otherwise strings '#N', 'N' and integers N are all accepted.
|
||||||
|
Output is the integer part.
|
||||||
"""
|
"""
|
||||||
if reqhash and not (isinstance(dbref, basestring) and dbref.startswith("#")):
|
if reqhash:
|
||||||
return None
|
return (int(dbref.lstrip('#')) if (isinstance(dbref, basestring) and
|
||||||
if isinstance(dbref, basestring):
|
dbref.startswith("#") and
|
||||||
|
dbref.lstrip('#').isdigit())
|
||||||
|
else None)
|
||||||
|
elif isinstance(dbref, basestring):
|
||||||
dbref = dbref.lstrip('#')
|
dbref = dbref.lstrip('#')
|
||||||
try:
|
return int(dbref) if dbref.isdigit() else None
|
||||||
dbref = int(dbref)
|
return dbref if isinstance(dbref, int) else None
|
||||||
if dbref < 1:
|
|
||||||
return None
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
return dbref
|
|
||||||
return None
|
|
||||||
|
|
||||||
def to_unicode(obj, encoding='utf-8', force_string=False):
|
def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue