2010-08-29 18:46:58 +00:00
"""
This is the * abstract * django models for many of the database objects
in Evennia . A django abstract ( obs , not the same as a Python metaclass ! ) is
a model which is not actually created in the database , but which only exists
for other models to inherit from , to avoid code duplication . Any model can
2012-03-30 23:47:22 +02:00
import and inherit from these classes .
2010-08-29 18:46:58 +00:00
Attributes are database objects stored on other objects . The implementing
class needs to supply a ForeignKey field attr_object pointing to the kind
2012-02-14 23:40:16 +01:00
of object being mapped . Attributes storing iterables actually store special
types of iterables named PackedList / PackedDict respectively . These make
2012-03-30 23:47:22 +02:00
sure to save changes to them to database - this is criticial in order to
allow for obj . db . mylist [ 2 ] = data . Also , all dbobjects are saved as
2012-02-14 23:40:16 +01:00
dbrefs but are also aggressively cached .
2010-08-29 18:46:58 +00:00
TypedObjects are objects ' decorated ' with a typeclass - that is , the typeclass
( which is a normal Python class implementing some special tricks with its
get / set attribute methods , allows for the creation of all sorts of different
objects all with the same database object underneath . Usually attributes are
used to permanently store things not hard - coded as field on the database object .
The admin should usually not have to deal directly with the database object
2012-03-30 23:47:22 +02:00
layer .
2010-08-29 18:46:58 +00:00
This module also contains the Managers for the respective models ; inherit from
2012-03-30 23:47:22 +02:00
these to create custom managers .
2010-08-29 18:46:58 +00:00
"""
2014-12-23 21:33:03 +01:00
from django . db . models import signals
2012-04-28 00:37:36 +02:00
2013-07-08 18:13:21 +02:00
from django . db import models
2014-06-15 12:27:48 +02:00
from django . core . exceptions import ObjectDoesNotExist
2011-02-28 23:43:14 +00:00
from django . conf import settings
2010-08-29 18:46:58 +00:00
from django . utils . encoding import smart_str
2013-08-24 21:23:43 +02:00
2014-12-23 22:25:39 +01:00
from src . typeclasses . attributes import Attribute , AttributeHandler , NAttributeHandler
from src . typeclasses . tags import Tag , TagHandler , AliasHandler , PermissionHandler
2014-07-05 20:32:08 +02:00
from src . utils . idmapper . models import SharedMemoryModel
2014-12-23 21:33:03 +01:00
from src . utils . idmapper . base import SharedMemoryModelBase
2013-10-21 21:17:32 +02:00
from src . server . caches import get_prop_cache , set_prop_cache
2014-02-16 21:27:42 +01:00
#from src.server.caches import set_attr_cache
2013-07-08 18:13:21 +02:00
2013-05-29 16:16:28 +02:00
#from src.server.caches import call_ndb_hooks
2014-12-20 19:19:48 +01:00
#from src.server.models import ServerConfig
2010-08-29 18:46:58 +00:00
from src . typeclasses import managers
2011-03-15 16:08:32 +00:00
from src . locks . lockhandler import LockHandler
2014-06-28 18:01:00 -05:00
from src . utils . utils import (
2014-12-25 18:58:11 +01:00
is_iter , inherits_from , lazy_property ,
2014-12-23 21:33:03 +01:00
class_from_module )
2014-12-23 22:25:39 +01:00
from src . typeclasses . django_new_patch import patched_new
2010-08-29 18:46:58 +00:00
2012-03-31 15:09:22 +02:00
__all__ = ( " Attribute " , " TypeNick " , " TypedObject " )
2014-06-14 19:31:19 +02:00
TICKER_HANDLER = None
2012-03-31 13:06:29 +02:00
_PERMISSION_HIERARCHY = [ p . lower ( ) for p in settings . PERMISSION_HIERARCHY ]
2013-10-20 21:02:37 +02:00
_TYPECLASS_AGGRESSIVE_CACHE = settings . TYPECLASS_AGGRESSIVE_CACHE
2014-12-24 01:24:26 +01:00
_GA = object . __getattribute__
_SA = object . __setattr__
2011-03-15 16:08:32 +00:00
2010-08-29 18:46:58 +00:00
#------------------------------------------------------------
#
2012-03-30 23:47:22 +02:00
# Typed Objects
2010-08-29 18:46:58 +00:00
#
2012-03-30 23:47:22 +02:00
#------------------------------------------------------------
2010-08-29 18:46:58 +00:00
2014-12-20 19:04:49 +01:00
#
# Meta class for typeclasses
#
2014-12-23 21:33:03 +01:00
# signal receivers. Assigned in __new__
def post_save ( sender , instance , created , * * kwargs ) :
"""
2014-12-25 14:43:43 +01:00
Receives a signal just after the object is saved .
2014-12-23 21:33:03 +01:00
"""
if created :
2014-12-25 14:43:43 +01:00
instance . at_first_save ( )
2014-12-23 21:33:03 +01:00
#TODO - put OOB handler here?
2014-12-20 19:04:49 +01:00
class TypeclassBase ( SharedMemoryModelBase ) :
"""
Metaclass which should be set for the root of model proxies
that don ' t define any new fields, like Object, Script etc.
"""
2014-12-20 19:29:38 +01:00
2014-12-20 19:04:49 +01:00
def __new__ ( cls , name , bases , attrs ) :
"""
2014-12-23 22:25:39 +01:00
We must define our Typeclasses as proxies . We also store the
path directly on the class , this is required by managers .
2014-12-20 19:04:49 +01:00
"""
2014-12-20 19:29:38 +01:00
2014-12-23 22:25:39 +01:00
# storage of stats
2015-01-02 21:41:29 +01:00
attrs [ " typename " ] = name #cls.__name__
2014-12-20 19:29:38 +01:00
attrs [ " path " ] = " %s . %s " % ( attrs [ " __module__ " ] , name )
2014-12-20 19:04:49 +01:00
# typeclass proxy setup
2014-12-21 15:02:34 +01:00
if not " Meta " in attrs :
2014-12-20 19:04:49 +01:00
class Meta :
proxy = True
attrs [ " Meta " ] = Meta
2014-12-21 15:02:34 +01:00
attrs [ " Meta " ] . proxy = True
2014-12-20 19:04:49 +01:00
2014-12-23 22:25:39 +01:00
# patch for django proxy multi-inheritance
2014-12-20 19:04:49 +01:00
# this is a copy of django.db.models.base.__new__
2014-12-23 22:25:39 +01:00
# with a few lines changed as per
# https://code.djangoproject.com/ticket/11560
new_class = patched_new ( cls , name , bases , attrs )
2014-12-20 19:04:49 +01:00
2014-12-23 21:33:03 +01:00
# attach signal
signals . post_save . connect ( post_save , sender = new_class )
2014-12-23 22:25:39 +01:00
2014-12-23 21:33:03 +01:00
return new_class
2014-12-20 19:04:49 +01:00
#
# Main TypedObject abstraction
#
2014-12-19 16:29:41 +01:00
2012-04-28 00:37:36 +02:00
2010-08-29 18:46:58 +00:00
class TypedObject ( SharedMemoryModel ) :
"""
Abstract Django model .
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
This is the basis for a typed object . It also contains all the
2012-03-30 23:47:22 +02:00
mechanics for managing connected attributes .
2010-08-29 18:46:58 +00:00
The TypedObject has the following properties :
key - main name
name - alias for key
typeclass_path - the path to the decorating typeclass
typeclass - auto - linked typeclass
date_created - time stamp of object creation
2012-03-30 23:47:22 +02:00
permissions - perm strings
dbref - #id of object
2010-08-29 18:46:58 +00:00
db - persistent attribute storage
2012-03-30 23:47:22 +02:00
ndb - non - persistent attribute storage
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
"""
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
#
2010-08-29 18:46:58 +00:00
# TypedObject Database Model setup
#
#
2013-11-14 19:31:17 +01:00
# 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)
2012-03-30 23:47:22 +02:00
2013-11-14 19:31:17 +01:00
# Main identifier of the object, for searching. Is accessed with self.key
# or self.name
2012-02-06 13:18:25 +01:00
db_key = models . CharField ( ' key ' , max_length = 255 , db_index = True )
2014-12-24 01:24:26 +01:00
# This is the python path to the type class this object is tied to. The
2013-11-14 19:31:17 +01:00
# typeclass is what defines what kind of Object this is)
2013-07-11 15:59:03 +02:00
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.
2011-10-02 01:21:03 +02:00
db_date_created = models . DateTimeField ( ' creation date ' , editable = False , auto_now_add = True )
2012-03-30 23:47:22 +02:00
# Lock storage
2013-07-11 15:59:03 +02:00
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. " )
2013-07-12 11:12:21 +02:00
# many2many relationships
2013-07-11 15:59:03 +02:00
db_attributes = models . ManyToManyField ( Attribute , null = True ,
2013-07-21 14:18:36 -05:00
help_text = ' attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases). ' )
2013-07-12 11:12:21 +02:00
db_tags = models . ManyToManyField ( Tag , null = True ,
help_text = ' tags on this object. Tags are simple string markers to identify, group and alias objects. ' )
2010-08-29 18:46:58 +00:00
# Database manager
objects = managers . TypedObjectManager ( )
2013-07-11 15:59:03 +02:00
# quick on-object typeclass cache for speed
2012-03-30 23:47:22 +02:00
_cached_typeclass = None
2011-08-06 18:15:04 +00:00
2014-12-19 16:29:41 +01:00
# typeclass mechanism
2011-03-15 16:08:32 +00:00
def __init__ ( self , * args , * * kwargs ) :
2014-12-24 01:24:26 +01:00
"""
This is the main function of the typeclass system -
to dynamically re - apply a class based on the
db_typeclass_path rather than use the one in the model .
"""
2014-12-19 16:29:41 +01:00
typeclass_path = kwargs . pop ( " typeclass " , None )
2014-05-15 09:51:24 +02:00
super ( TypedObject , self ) . __init__ ( * args , * * kwargs )
2014-12-19 16:29:41 +01:00
if typeclass_path :
2014-12-23 21:33:03 +01:00
self . __class__ = class_from_module ( typeclass_path )
2014-12-19 16:29:41 +01:00
self . db_typclass_path = typeclass_path
elif self . db_typeclass_path :
2014-12-23 21:33:03 +01:00
self . __class__ = class_from_module ( self . db_typeclass_path )
2014-12-19 16:29:41 +01:00
else :
self . db_typeclass_path = " %s . %s " % ( self . __module__ , self . __class__ . __name__ )
2014-12-22 21:27:32 +01:00
# important to put this at the end since _meta is based on the set __class__
2014-12-25 18:58:11 +01:00
self . __dbclass__ = self . _meta . proxy_for_model or self . __class__
2014-07-06 13:10:03 +02:00
# initialize all handlers in a lazy fashion
@lazy_property
def attributes ( self ) :
return AttributeHandler ( self )
@lazy_property
def locks ( self ) :
return LockHandler ( self )
@lazy_property
def tags ( self ) :
return TagHandler ( self )
@lazy_property
def aliases ( self ) :
return AliasHandler ( self )
@lazy_property
def permissions ( self ) :
return PermissionHandler ( self )
@lazy_property
def nattributes ( self ) :
return NAttributeHandler ( self )
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
class Meta :
"""
Django setup info .
"""
2012-03-30 23:47:22 +02:00
abstract = True
2010-08-29 18:46:58 +00:00
verbose_name = " Evennia Database Object "
ordering = [ ' -db_date_created ' , ' id ' , ' db_typeclass_path ' , ' db_key ' ]
2012-03-30 23:47:22 +02:00
2013-07-11 15:59:03 +02:00
# wrapper
2010-08-29 18:46:58 +00:00
# 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()
# etc). So e.g. a property 'attr' has a get/set/del decorator
2012-03-30 23:47:22 +02:00
# defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self
2010-08-29 18:46:58 +00:00
# is the object in question).
2013-07-11 15:59:03 +02:00
# name property (alias to self.key)
2013-11-14 19:31:17 +01:00
def __name_get ( self ) :
return self . key
def __name_set ( self , value ) :
self . key = value
def __name_del ( self ) :
raise Exception ( " Cannot delete name " )
2012-03-31 13:06:29 +02:00
name = property ( __name_get , __name_set , __name_del )
2010-08-29 18:46:58 +00:00
#
#
2012-03-30 23:47:22 +02:00
# TypedObject main class methods and properties
2010-08-29 18:46:58 +00:00
#
#
2012-03-30 23:47:22 +02:00
def __eq__ ( self , other ) :
2012-04-26 17:47:25 +02:00
return other and hasattr ( other , ' dbid ' ) and self . dbid == other . dbid
2010-08-29 18:46:58 +00:00
def __str__ ( self ) :
2014-12-23 22:25:39 +01:00
return smart_str ( " %s " % self . db_key )
2010-08-29 18:46:58 +00:00
def __unicode__ ( self ) :
2014-12-23 22:25:39 +01:00
return u " %s " % self . db_key
2010-08-29 18:46:58 +00:00
2012-04-26 17:47:25 +02:00
#@property
def __dbid_get ( self ) :
"""
Caches and returns the unique id of the object .
Use this instead of self . id , which is not cached .
"""
2012-11-01 11:20:07 +01:00
dbid = get_prop_cache ( self , " _dbid " )
2012-04-26 17:47:25 +02:00
if not dbid :
2014-12-23 22:25:39 +01:00
dbid = self . id
2012-11-01 11:20:07 +01:00
set_prop_cache ( self , " _dbid " , dbid )
2012-04-26 17:47:25 +02:00
return dbid
2013-11-14 19:31:17 +01:00
2012-04-26 17:47:25 +02:00
def __dbid_set ( self , value ) :
raise Exception ( " dbid cannot be set! " )
2013-11-14 19:31:17 +01:00
2012-04-26 17:47:25 +02:00
def __dbid_del ( self ) :
raise Exception ( " dbid cannot be deleted! " )
dbid = property ( __dbid_get , __dbid_set , __dbid_del )
2010-08-29 18:46:58 +00:00
#@property
2012-03-31 13:06:29 +02:00
def __dbref_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
2012-04-26 17:47:25 +02:00
Returns the object ' s dbref on the form #NN.
2010-08-29 18:46:58 +00:00
"""
2014-12-28 20:00:31 +01:00
return " # %s " % self . dbid
2013-11-14 19:31:17 +01:00
2012-04-26 17:47:25 +02:00
def __dbref_set ( self ) :
raise Exception ( " dbref cannot be set! " )
2013-11-14 19:31:17 +01:00
2012-04-26 17:47:25 +02:00
def __dbref_del ( self ) :
raise Exception ( " dbref cannot be deleted! " )
dbref = property ( __dbref_get , __dbref_set , __dbref_del )
2010-08-29 18:46:58 +00:00
#
# Object manipulation methods
2012-03-30 23:47:22 +02:00
#
2010-08-29 18:46:58 +00:00
2014-04-21 11:52:48 +02:00
def swap_typeclass ( self , new_typeclass , clean_attributes = False ,
run_start_hooks = True , no_default = True ) :
2010-08-29 18:46:58 +00:00
"""
This performs an in - situ swap of the typeclass . This means
that in - game , this object will suddenly be something else .
Player will not be affected . To ' move ' a player to a different
2012-03-30 23:47:22 +02:00
object entirely ( while retaining this object ' s type), use
2010-08-29 18:46:58 +00:00
self . player . swap_object ( ) .
2012-03-30 23:47:22 +02:00
Note that this might be an error prone operation if the
2010-08-29 18:46:58 +00:00
old / new typeclass was heavily customized - your code
2012-03-30 23:47:22 +02:00
might expect one and not the other , so be careful to
2010-08-29 18:46:58 +00:00
bug test your code if using this feature ! Often its easiest
to create a new object and just swap the player over to
2012-03-30 23:47:22 +02:00
that one instead .
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
Arguments :
new_typeclass ( path / classobj ) - type to switch to
2010-08-29 18:46:58 +00:00
clean_attributes ( bool / list ) - will delete all attributes
stored on this object ( but not any
of the database fields such as name or
location ) . You can ' t get attributes back,
but this is often the safest bet to make
sure nothing in the new typeclass clashes
with the old one . If you supply a list ,
only those named attributes will be cleared .
2015-01-02 19:01:09 +01:00
run_start_hooks - trigger the start hooks of the object , as if
it was created for the first time .
2011-10-03 23:53:23 +02:00
no_default - if this is active , the swapper will not allow for
swapping to a default typeclass in case the given
one fails for some reason . Instead the old one
2012-03-30 23:47:22 +02:00
will be preserved .
Returns :
2012-03-25 16:35:22 +02:00
boolean True / False depending on if the swap worked or not .
2010-08-29 18:46:58 +00:00
"""
2014-12-24 01:24:26 +01:00
if not callable ( new_typeclass ) :
2010-08-29 18:46:58 +00:00
# this is an actual class object - build the path
2014-12-24 01:24:26 +01:00
new_typeclass = class_from_module ( new_typeclass )
# if we get to this point, the class is ok.
2010-08-29 18:46:58 +00:00
2014-04-21 11:52:48 +02:00
if inherits_from ( self , " src.scripts.models.ScriptDB " ) :
if self . interval > 0 :
raise RuntimeError ( " Cannot use swap_typeclass on time-dependent " \
" Script ' %s ' . \n Stop and start a new Script of the " \
" right type instead. " % self . key )
2014-12-24 01:24:26 +01:00
self . typeclass_path = new_typeclass . path
2014-12-25 18:58:11 +01:00
self . __class__ = new_typeclass
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
if clean_attributes :
# Clean out old attributes
if is_iter ( clean_attributes ) :
for attr in clean_attributes :
2014-08-04 12:15:10 +02:00
self . attributes . remove ( attr )
2010-08-29 18:46:58 +00:00
for nattr in clean_attributes :
if hasattr ( self . ndb , nattr ) :
2014-08-04 12:15:10 +02:00
self . nattributes . remove ( nattr )
2010-08-29 18:46:58 +00:00
else :
#print "deleting attrs ..."
2014-08-04 12:15:10 +02:00
self . attributes . clear ( )
self . nattributes . clear ( )
2010-08-29 18:46:58 +00:00
2014-04-21 11:52:48 +02:00
if run_start_hooks :
2014-12-25 14:43:43 +01:00
# fake this call to mimic the first save
self . at_first_save ( )
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
#
2013-08-24 23:57:44 +02:00
# Lock / permission methods
#
2014-01-18 23:56:07 +01:00
def access ( self , accessing_obj , access_type = ' read ' , default = False , * * kwargs ) :
2013-08-24 23:57:44 +02:00
"""
Determines if another object has permission to access .
accessing_obj - object trying to access this one
access_type - type of access sought
default - what to return if no lock of access_type was found
2014-01-18 23:56:07 +01:00
* * kwargs - this is ignored , but is there to make the api consistent with the
object - typeclass method access , which use it to feed to its hook methods .
2013-08-24 23:57:44 +02:00
"""
return self . locks . check ( accessing_obj , access_type = access_type , default = default )
def check_permstring ( self , permstring ) :
"""
2013-11-14 19:31:17 +01:00
This explicitly checks if we hold particular permission without
2014-01-18 23:56:07 +01:00
involving any locks . It does - not - trigger the at_access hook .
2013-08-24 23:57:44 +02:00
"""
if hasattr ( self , " player " ) :
2013-11-14 19:31:17 +01:00
if self . player and self . player . is_superuser :
return True
2013-08-24 23:57:44 +02:00
else :
2013-11-14 19:31:17 +01:00
if self . is_superuser :
return True
2013-08-24 23:57:44 +02:00
if not permstring :
return False
perm = permstring . lower ( )
perms = [ p . lower ( ) for p in self . permissions . all ( ) ]
if perm in perms :
# simplest case - we have a direct match
return True
if perm in _PERMISSION_HIERARCHY :
# check if we have a higher hierarchy position
ppos = _PERMISSION_HIERARCHY . index ( perm )
return any ( True for hpos , hperm in enumerate ( _PERMISSION_HIERARCHY )
if hperm in perms and hpos > ppos )
return False
2014-06-15 12:27:48 +02:00
#
# Deletion methods
#
def _deleted ( self , * args , * * kwargs ) :
" Scrambling method for already deleted objects "
raise ObjectDoesNotExist ( " This object was already deleted! " )
_is_deleted = False # this is checked by db_* wrappers
2014-04-20 16:47:03 +02:00
def delete ( self ) :
" Cleaning up handlers on the typeclass level "
2014-06-14 19:31:19 +02:00
global TICKER_HANDLER
if not TICKER_HANDLER :
from src . scripts . tickerhandler import TICKER_HANDLER
2014-06-15 12:27:48 +02:00
TICKER_HANDLER . remove ( self ) # removes objects' all ticker subscriptions
2014-12-23 22:25:39 +01:00
self . permissions . clear ( )
self . attributes . clear ( )
self . aliases . clear ( )
2014-07-06 13:10:03 +02:00
if hasattr ( self , " nicks " ) :
2014-12-23 22:25:39 +01:00
self . nicks . clear ( )
self . flush_from_cache ( )
2014-06-15 12:27:48 +02:00
# scrambling properties
self . delete = self . _deleted
self . _is_deleted = True
2014-04-20 16:47:03 +02:00
super ( TypedObject , self ) . delete ( )
2013-10-15 20:00:18 +02:00
#
# Memory management
#
2013-08-24 23:57:44 +02:00
def flush_from_cache ( self ) :
"""
2013-11-14 19:31:17 +01:00
Flush this object instance from cache , forcing an object reload .
Note that this will kill all temporary attributes on this object
since it will be recreated as a new Typeclass instance .
2013-08-24 23:57:44 +02:00
"""
self . __class__ . flush_cached_instance ( self )
2013-10-15 20:00:18 +02:00
#
# Attribute storage
#
#@property db
def __db_get ( self ) :
"""
Attribute handler wrapper . Allows for the syntax
obj . db . attrname = value
and
value = obj . db . attrname
and
del obj . db . attrname
and
2013-11-14 19:31:17 +01:00
all_attr = obj . db . all ( ) ( unless there is an attribute
named ' all ' , in which case that will be returned instead ) .
2013-10-15 20:00:18 +02:00
"""
try :
return self . _db_holder
except AttributeError :
class DbHolder ( object ) :
" Holder for allowing property access of attributes "
def __init__ ( self , obj ) :
2014-12-24 01:24:26 +01:00
_SA ( self , " attrhandler " , obj . attributes )
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
def __getattribute__ ( self , attrname ) :
if attrname == ' all ' :
# we allow to overload our default .all
2014-12-24 01:24:26 +01:00
attr = _GA ( self , " attrhandler " ) . get ( " all " )
2013-10-15 20:00:18 +02:00
if attr :
return attr
2014-12-23 22:25:39 +01:00
return self . all
2014-12-24 01:24:26 +01:00
return _GA ( self , " attrhandler " ) . get ( attrname )
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
def __setattr__ ( self , attrname , value ) :
2014-12-24 01:24:26 +01:00
_GA ( self , " attrhandler " ) . add ( attrname , value )
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
def __delattr__ ( self , attrname ) :
2014-12-24 01:24:26 +01:00
_GA ( self , " attrhandler " ) . remove ( attrname )
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
def get_all ( self ) :
2014-12-24 01:24:26 +01:00
return _GA ( self , " attrhandler " ) . all ( )
2013-10-15 20:00:18 +02:00
all = property ( get_all )
self . _db_holder = DbHolder ( self )
return self . _db_holder
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
#@db.setter
def __db_set ( self , value ) :
" Stop accidentally replacing the db object "
string = " Cannot assign directly to db object! "
string + = " Use db.attr=value instead. "
raise Exception ( string )
2013-11-14 19:31:17 +01:00
2013-10-15 20:00:18 +02:00
#@db.deleter
def __db_del ( self ) :
" Stop accidental deletion. "
raise Exception ( " Cannot delete the db object! " )
db = property ( __db_get , __db_set , __db_del )
2013-08-24 23:57:44 +02:00
#
# Non-persistent (ndb) storage
#
#@property ndb
def __ndb_get ( self ) :
"""
A non - attr_obj store ( ndb : NonDataBase ) . Everything stored
to this is guaranteed to be cleared when a server is shutdown .
Syntax is same as for the _get_db_holder ( ) method and
property , e . g . obj . ndb . attr = value etc .
"""
try :
return self . _ndb_holder
except AttributeError :
2014-05-11 01:05:59 +02:00
class NDbHolder ( object ) :
" Holder for allowing property access of attributes "
def __init__ ( self , obj ) :
2014-12-24 01:24:26 +01:00
_SA ( self , " nattrhandler " , obj . nattributes )
2013-11-14 19:31:17 +01:00
2014-05-11 01:05:59 +02:00
def __getattribute__ ( self , attrname ) :
if attrname == ' all ' :
# we allow to overload our default .all
2014-12-24 01:24:26 +01:00
attr = _GA ( self , " nattrhandler " ) . get ( ' all ' )
2014-05-11 01:05:59 +02:00
if attr :
return attr
2014-12-23 22:25:39 +01:00
return self . all
2014-12-24 01:24:26 +01:00
return _GA ( self , " nattrhandler " ) . get ( attrname )
2014-05-11 01:05:59 +02:00
def __setattr__ ( self , attrname , value ) :
2014-12-24 01:24:26 +01:00
_GA ( self , " nattrhandler " ) . add ( attrname , value )
2013-11-14 19:31:17 +01:00
2014-05-11 01:05:59 +02:00
def __delattr__ ( self , attrname ) :
2014-12-24 01:24:26 +01:00
_GA ( self , " nattrhandler " ) . remove ( attrname )
2014-05-11 01:05:59 +02:00
def get_all ( self ) :
2014-12-24 01:24:26 +01:00
return _GA ( self , " nattrhandler " ) . all ( )
2014-05-11 01:05:59 +02:00
all = property ( get_all )
self . _ndb_holder = NDbHolder ( self )
2013-08-24 23:57:44 +02:00
return self . _ndb_holder
2013-11-14 19:31:17 +01:00
2014-05-11 01:05:59 +02:00
#@db.setter
2013-08-24 23:57:44 +02:00
def __ndb_set ( self , value ) :
2014-05-11 01:05:59 +02:00
" Stop accidentally replacing the ndb object "
2013-08-24 23:57:44 +02:00
string = " Cannot assign directly to ndb object! "
2014-05-11 01:05:59 +02:00
string + = " Use ndb.attr=value instead. "
2013-08-24 23:57:44 +02:00
raise Exception ( string )
2013-11-14 19:31:17 +01:00
2014-05-11 01:05:59 +02:00
#@db.deleter
2013-08-24 23:57:44 +02:00
def __ndb_del ( self ) :
" Stop accidental deletion. "
raise Exception ( " Cannot delete the ndb object! " )
ndb = property ( __ndb_get , __ndb_set , __ndb_del )