First version with all caches seemingly working ok. Started to remove the on-model wrappers that are not handled by the idmapper metaclass.

This commit is contained in:
Griatch 2013-07-11 15:59:03 +02:00
parent f1ba0ca177
commit 9e10a41e18
10 changed files with 144 additions and 165 deletions

View file

@ -303,8 +303,6 @@ class Msg(SharedMemoryModel):
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) 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) 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 # Msg class methods
# #

View file

@ -45,7 +45,7 @@ class HelpEntry(SharedMemoryModel):
# These database fields are all set using their corresponding properties, # These database fields are all set using their corresponding properties,
# named same as the field, but withtout the db_* prefix. # 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') db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for')
# help category # help category
db_help_category = models.CharField("help category", max_length=255, default="General", 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 = "Help Entry"
verbose_name_plural = "Help Entries" 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 # 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
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()
@ -85,53 +82,53 @@ class HelpEntry(SharedMemoryModel):
# key property (wraps db_key) # key property (wraps db_key)
#@property #@property
def __key_get(self): #def __key_get(self):
"Getter. Allows for value = self.key" # "Getter. Allows for value = self.key"
return self.db_key # return self.db_key
#@key.setter ##@key.setter
def __key_set(self, value): #def __key_set(self, value):
"Setter. Allows for self.key = value" # "Setter. Allows for self.key = value"
self.db_key = value # self.db_key = value
self.save() # self.save()
#@key.deleter ##@key.deleter
def __key_del(self): #def __key_del(self):
"Deleter. Allows for del self.key. Deletes entry." # "Deleter. Allows for del self.key. Deletes entry."
self.delete() # self.delete()
key = property(__key_get, __key_set, __key_del) #key = property(__key_get, __key_set, __key_del)
# help_category property (wraps db_help_category) ## help_category property (wraps db_help_category)
#@property ##@property
def __help_category_get(self): #def __help_category_get(self):
"Getter. Allows for value = self.help_category" # "Getter. Allows for value = self.help_category"
return self.db_help_category # return self.db_help_category
#@help_category.setter ##@help_category.setter
def __help_category_set(self, value): #def __help_category_set(self, value):
"Setter. Allows for self.help_category = value" # "Setter. Allows for self.help_category = value"
self.db_help_category = value # self.db_help_category = value
self.save() # self.save()
#@help_category.deleter ##@help_category.deleter
def __help_category_del(self): #def __help_category_del(self):
"Deleter. Allows for del self.help_category" # "Deleter. Allows for del self.help_category"
self.db_help_category = "General" # self.db_help_category = "General"
self.save() # self.save()
help_category = property(__help_category_get, __help_category_set, __help_category_del) #help_category = property(__help_category_get, __help_category_set, __help_category_del)
# entrytext property (wraps db_entrytext) ## entrytext property (wraps db_entrytext)
#@property ##@property
def __entrytext_get(self): #def __entrytext_get(self):
"Getter. Allows for value = self.entrytext" # "Getter. Allows for value = self.entrytext"
return self.db_entrytext # return self.db_entrytext
#@entrytext.setter ##@entrytext.setter
def __entrytext_set(self, value): #def __entrytext_set(self, value):
"Setter. Allows for self.entrytext = value" # "Setter. Allows for self.entrytext = value"
self.db_entrytext = value # self.db_entrytext = value
self.save() # self.save()
#@entrytext.deleter ##@entrytext.deleter
def __entrytext_del(self): #def __entrytext_del(self):
"Deleter. Allows for del self.entrytext" # "Deleter. Allows for del self.entrytext"
self.db_entrytext = "" # self.db_entrytext = ""
self.save() # self.save()
entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del) #entrytext = property(__entrytext_get, __entrytext_set, __entrytext_del)
# permissions property # permissions property
#@property #@property
@ -153,20 +150,20 @@ class HelpEntry(SharedMemoryModel):
permissions = property(__permissions_get, __permissions_set, __permissions_del) permissions = property(__permissions_get, __permissions_set, __permissions_del)
# lock_storage property (wraps db_lock_storage) # lock_storage property (wraps db_lock_storage)
#@property ##@property
def __lock_storage_get(self): #def __lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" # "Getter. Allows for value = self.lock_storage"
return self.db_lock_storage # return self.db_lock_storage
#@nick.setter ##@nick.setter
def __lock_storage_set(self, value): #def __lock_storage_set(self, value):
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" # """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
self.db_lock_storage = value # self.db_lock_storage = value
self.save() # self.save()
#@nick.deleter ##@nick.deleter
def __lock_storage_del(self): #def __lock_storage_del(self):
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" # "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) # 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) #lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del)
# #

View file

@ -6,7 +6,7 @@
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import admin 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.objects.models import ObjectDB, ObjectNick, Alias
from src.utils.utils import mod_import from src.utils.utils import mod_import

View file

@ -187,8 +187,11 @@ class ObjectDB(TypedObject):
# Database manager # Database manager
objects = ObjectManager() 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): def __init__(self, *args, **kwargs):
"Parent must be initialized first." "Parent must be initialized first."
TypedObject.__init__(self, *args, **kwargs) TypedObject.__init__(self, *args, **kwargs)
@ -495,11 +498,6 @@ class ObjectDB(TypedObject):
# ObjectDB class access methods/properties # 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 #@property
def __sessions_get(self): def __sessions_get(self):

View file

@ -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") 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. # database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, 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 # Database manager
objects = manager.PlayerManager() 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: class Meta:
app_label = 'players' app_label = 'players'
verbose_name = 'Player' verbose_name = 'Player'
@ -229,11 +233,6 @@ class PlayerDB(TypedObject):
def __unicode__(self): def __unicode__(self):
return u"%s(player#%s)" % (_GA(self, "name"), _GA(self, "dbid")) 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) # name property (wraps self.user.username)
#@property #@property

View file

@ -96,6 +96,10 @@ class ScriptDB(TypedObject):
# Database manager # Database manager
objects = ScriptManager() 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: class Meta:
"Define Django meta options" "Define Django meta options"
verbose_name = "Script" 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): def at_typeclass_error(self):
""" """

View file

@ -90,7 +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:", 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: 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 = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) 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'] obj = kwargs['instance']
model = kwargs['model'] model = kwargs['model']
action = kwargs['action'] action = kwargs['action']
print "update_attr_cache:", obj, model, action #print "update_attr_cache:", obj, model, action
if kwargs['reverse']: if kwargs['reverse']:
# the reverse relation changed (the Attribute itself was acted on) # the reverse relation changed (the Attribute itself was acted on)
pass pass

View file

@ -48,9 +48,6 @@ class ServerConfig(SharedMemoryModel):
objects = ServerConfigManager() objects = ServerConfigManager()
# used by Attributes eventually storing this safely
_db_model_name = "serverconfig"
# 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
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()

View file

@ -401,29 +401,29 @@ class TypedObject(SharedMemoryModel):
# TypedObject Database Model setup # TypedObject Database Model setup
# #
# #
# These databse fields are all set using their corresponding properties, # These databse fields are all accessed and set using their corresponding properties,
# named same as the field, but withtou the db_* prefix. # 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 # Main identifier of the object, for searching. Is accessed with self.key or self.name
# be referenced as 'name'.
db_key = models.CharField('key', max_length=255, db_index=True) 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 # 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)
# (the type class is what defines what kind of Object this is) db_typeclass_path = models.CharField('typeclass', max_length=255, null=True,
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.") 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 # Creation date. This is not changed once the object is created.
db_date_created = models.DateTimeField('creation date', editable=False, auto_now_add=True) db_date_created = models.DateTimeField('creation date', editable=False, auto_now_add=True)
# Permissions (access these through the 'permissions' property) # 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 # 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.")
# attribute store # 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).') 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()
# object cache and flags # quick on-object typeclass cache for speed
_cached_typeclass = None _cached_typeclass = None
# lock handler self.locks # lock handler self.locks
@ -441,6 +441,7 @@ class TypedObject(SharedMemoryModel):
verbose_name = "Evennia Database Object" verbose_name = "Evennia Database Object"
ordering = ['-db_date_created', 'id', 'db_typeclass_path', 'db_key'] ordering = ['-db_date_created', 'id', 'db_typeclass_path', 'db_key']
# wrapper
# 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
# normal python operations (without having to remember to save() # 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 # value = self.attr and del self.attr respectively (where self
# is the object in question). # is the object in question).
# key property (wraps db_key) # key property (wraps db_key)
#@property #@property
#def __key_get(self): #def __key_get(self):
@ -466,53 +466,44 @@ class TypedObject(SharedMemoryModel):
# raise Exception("Cannot delete objectdb key!") # raise Exception("Cannot delete objectdb key!")
#key = property(__key_get, __key_set, __key_del) #key = property(__key_get, __key_set, __key_del)
# name property (wraps db_key too - alias to self.key) # name property (alias to self.key)
#@property def __name_get(self): return self.key
def __name_get(self): def __name_set(self, value): self.key = value
"Getter. Allows for value = self.name" def __name_del(self): raise Exception("Cannot delete 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(__name_get, __name_set, __name_del) name = property(__name_get, __name_set, __name_del)
# typeclass_path property - we don't cache this. # typeclass_path property - we manage this separately.
#@property #@property
def __typeclass_path_get(self): #def __typeclass_path_get(self):
"Getter. Allows for value = self.typeclass_path" # "Getter. Allows for value = self.typeclass_path"
return _GA(self, "db_typeclass_path")#get_field_cache(self, "typeclass_path") # return _GA(self, "db_typeclass_path")
#@typeclass_path.setter ##@typeclass_path.setter
def __typeclass_path_set(self, value): #def __typeclass_path_set(self, value):
"Setter. Allows for self.typeclass_path = value" # "Setter. Allows for self.typeclass_path = value"
_SA(self, "db_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 # 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) # _GA(self, "save")(update_fields=update_fields)
#@typeclass_path.deleter ##@typeclass_path.deleter
def __typeclass_path_del(self): #def __typeclass_path_del(self):
"Deleter. Allows for del self.typeclass_path" # "Deleter. Allows for del self.typeclass_path"
self.db_typeclass_path = "" # self.db_typeclass_path = ""
_GA(self, "save")(update_fields=["db_typeclass_path"]) # _GA(self, "save")(update_fields=["db_typeclass_path"])
typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del) #typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
# date_created property # date_created property
#@property #@property
def __date_created_get(self): #def __date_created_get(self):
"Getter. Allows for value = self.date_created" # "Getter. Allows for value = self.date_created"
return get_field_cache(self, "date_created") # return get_field_cache(self, "date_created")
#@date_created.setter ##@date_created.setter
def __date_created_set(self, value): #def __date_created_set(self, value):
"Setter. Allows for self.date_created = value" # "Setter. Allows for self.date_created = value"
raise Exception("Cannot change date_created!") # raise Exception("Cannot change date_created!")
#@date_created.deleter ##@date_created.deleter
def __date_created_del(self): #def __date_created_del(self):
"Deleter. Allows for del self.date_created" # "Deleter. Allows for del self.date_created"
raise Exception("Cannot delete date_created!") # raise Exception("Cannot delete date_created!")
date_created = property(__date_created_get, __date_created_set, __date_created_del) #date_created = property(__date_created_get, __date_created_set, __date_created_del)
# permissions property # permissions property
#@property #@property
@ -537,18 +528,18 @@ class TypedObject(SharedMemoryModel):
# lock_storage property (wraps db_lock_storage) # lock_storage property (wraps db_lock_storage)
#@property #@property
def __lock_storage_get(self): #def __lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" # "Getter. Allows for value = self.lock_storage"
return get_field_cache(self, "lock_storage") # return get_field_cache(self, "lock_storage")
#@lock_storage.setter ##@lock_storage.setter
def __lock_storage_set(self, value): #def __lock_storage_set(self, value):
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" # """Saves the lock_storage. This is usually not called directly, but through self.lock()"""
set_field_cache(self, "lock_storage", value) # set_field_cache(self, "lock_storage", value)
#@lock_storage.deleter ##@lock_storage.deleter
def __lock_storage_del(self): #def __lock_storage_del(self):
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" # "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) # 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) #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 # these are identifiers for fast Attribute access and caching
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS _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): def __eq__(self, other):
return other and hasattr(other, 'dbid') and self.dbid == other.dbid return other and hasattr(other, 'dbid') and self.dbid == other.dbid

View file

@ -87,18 +87,18 @@ class SharedMemoryModelBase(ModelBase):
"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):
"Wrapper for getting database field" "Wrapper for getting database field"
#print "_get wrapper:", fname, value, type(value)
value = _GA(cls, fieldname) value = _GA(cls, fieldname)
if isinstance(value, (basestring, int, float, bool)): if type(value) in (basestring, int, float, bool):
return value return value
elif hasattr(value, "typeclass"): elif hasattr(value, "typeclass"):
return _GA(value, "typeclass") return _GA(value, "typeclass")
return value return value
def _set(cls, fname, value): def _set(cls, fname, value):
"Wrapper for setting database field" "Wrapper for setting database field"
if hasattr(value, "dbobj"): if hasattr(value, "dbobj"):
value = _GA(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 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 # (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) # not a custom handler must be used for that field)
@ -107,25 +107,27 @@ class SharedMemoryModelBase(ModelBase):
try: try:
value = cls._default_manager.get(id=dbid) value = cls._default_manager.get(id=dbid)
except ObjectDoesNotExist: except ObjectDoesNotExist:
err = "Could not set %s. Tried to treat value '%s' as a dbref, but no matching object with that id was found." # maybe it is just a name
err = err % (fname, value) pass
raise ObjectDoesNotExist(err)
#print "_set wrapper:", fname, value, type(value), cls._get_pk_val(cls._meta) #print "_set wrapper:", fname, value, type(value), cls._get_pk_val(cls._meta)
_SA(cls, fname, value) _SA(cls, fname, value)
# only use explicit update_fields in save if we actually have a # only use explicit update_fields in save if we actually have a
# primary key assigned already (won't be when first creating object) # 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 update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None
_GA(cls, "save")(update_fields=update_fields) _GA(cls, "save")(update_fields=update_fields)
def _del(cls, fname): def _del(cls, fname):
"Wrapper for clearing database field - sets it to None" "Wrapper for clearing database field - sets it to None"
_SA(cls, fname, None) _SA(cls, fname, None)
update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else 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) _GA(cls, "save")(update_fields=update_fields)
# create class wrappers # create class wrappers
fget = lambda cls: _get(cls, fieldname) fget = lambda cls: _get(cls, fieldname)
fset = lambda cls, val: _set(cls, fieldname, val) fset = lambda cls, val: _set(cls, fieldname, val)
fdel = lambda cls: _del(cls, fieldname) fdel = lambda cls: _del(cls, fieldname)
doc = "Wraps setting, saving and deleting the %s field." % fieldname doc = "Wraps setting, saving and deleting the %s field." % fieldname
type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel, doc)) type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel, doc))
# exclude some models that should not auto-create wrapper fields # exclude some models that should not auto-create wrapper fields