Removed LiteAttributes, made Nicks use Attributes. Added category and strvalue fields to Attribute. Made Attributes accessible through an AttributeHandler, like most other advanced properties.

This commit is contained in:
Griatch 2013-08-24 21:23:43 +02:00
parent befe6a6db0
commit 2f5c895f76
9 changed files with 609 additions and 420 deletions

View file

@ -18,7 +18,7 @@ import traceback
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from src.typeclasses.models import TypedObject, TagHandler, NickHandler, AliasHandler from src.typeclasses.models import TypedObject, TagHandler, NickHandler, AliasHandler, AttributeHandler
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 from src.server.caches import get_prop_cache, set_prop_cache
@ -140,9 +140,10 @@ class ObjectDB(TypedObject):
_SA(self, "cmdset", CmdSetHandler(self)) _SA(self, "cmdset", CmdSetHandler(self))
_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, "tags", TagHandler(self, category_prefix="object_")) _SA(self, "attributes", AttributeHandler(self))
_SA(self, "aliases", AliasHandler(self, category_prefix="object_")) _SA(self, "tags", TagHandler(self))
_SA(self, "nicks", NickHandler(self, category_prefix="object_")) _SA(self, "aliases", AliasHandler(self))
_SA(self, "nicks", NickHandler(self))
# make sure to sync the contents cache when initializing # make sure to sync the contents cache when initializing
self.contents_update() self.contents_update()
@ -646,10 +647,10 @@ class ObjectDB(TypedObject):
raw_list = raw_string.split(None) raw_list = raw_string.split(None)
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]] raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
# fetch the nick data efficiently # fetch the nick data efficiently
nicks = self.db_liteattributes.filter(db_category__in=("object_nick_inputline", "object_nick_channel")).prefetch_related("db_key","db_data") nicks = self.db_attributes.filter(db_category__in=("nick_inputline", "nick_channel")).prefetch_related("db_key","db_strvalue")
if self.has_player: if self.has_player:
pnicks = self.player.db_liteattributes.filter( pnicks = self.player.db_attributes.filter(
db_category__in=("player_nick_inputline", "player_nick_channel")).prefetch_related("db_key","db_data") db_category__in=("nick_inputline", "nick_channel")).prefetch_related("db_key","db_strvalue")
nicks = list(nicks) + list(pnicks) nicks = list(nicks) + list(pnicks)
for nick in nicks: for nick in nicks:
if nick.db_key in raw_list: if nick.db_key in raw_list:

View file

@ -25,7 +25,7 @@ from src.server.caches import get_field_cache, set_field_cache
from src.players import manager from src.players import manager
from src.scripts.models import ScriptDB from src.scripts.models import ScriptDB
from src.typeclasses.models import TypedObject, TagHandler, NickHandler, AliasHandler from src.typeclasses.models import TypedObject, TagHandler, NickHandler, AliasHandler, AttributeHandler
from src.commands.cmdsethandler import CmdSetHandler from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler from src.commands import cmdhandler
from src.utils import utils from src.utils import utils
@ -113,9 +113,10 @@ class PlayerDB(TypedObject, AbstractUser):
# handlers # handlers
_SA(self, "cmdset", CmdSetHandler(self)) _SA(self, "cmdset", CmdSetHandler(self))
_GA(self, "cmdset").update(init_mode=True) _GA(self, "cmdset").update(init_mode=True)
_SA(self, "attributes", AttributeHandler(self))
_SA(self, "tags", TagHandler(self, category_prefix="player_")) _SA(self, "tags", TagHandler(self, category_prefix="player_"))
_SA(self, "aliases", AliasHandler(self, category_prefix="player_")) _SA(self, "aliases", AliasHandler(self, category_prefix="player_"))
_SA(self, "nicks", NickHandler(self, category_prefix="player_")) _SA(self, "nicks", NickHandler(self))
# 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
@ -483,8 +484,8 @@ class PlayerDB(TypedObject, AbstractUser):
raw_list = raw_string.split(None) raw_list = raw_string.split(None)
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]] raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
# get the nick replacement data directly from the database to be able to use db_category__in # get the nick replacement data directly from the database to be able to use db_category__in
nicks = self.db_liteattributes.filter( nicks = self.db_attributes.filter(
db_category__in=("object_nick_inputline", "object_nick_channel")).prefetch_related("db_key","db_data") db_category__in=("nick_inputline", "nick_channel")).prefetch_related("db_key","db_strvalue")
for nick in nicks: for nick in nicks:
if nick.db_key in raw_list: if nick.db_key in raw_list:
raw_string = raw_string.replace(nick.db_key, nick.db_data, 1) raw_string = raw_string.replace(nick.db_key, nick.db_data, 1)

View file

@ -28,7 +28,7 @@ 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.typeclasses.models import Attribute, TypedObject, TagHandler, AliasHandler, NickHandler from src.typeclasses.models import Attribute, TypedObject, TagHandler, AttributeHandler#, AliasHandler, NickHandler
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
@ -108,7 +108,8 @@ class ScriptDB(TypedObject):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ScriptDB, self).__init__(*args, **kwargs) super(ScriptDB, self).__init__(*args, **kwargs)
_SA(self, "tags", TagHandler(self, category_prefix="script_")) _SA(self, "tags", TagHandler(self, category_prefix="script_"))
_SA(self, "aliases", AliasHandler(self, category_prefix="script_")) _SA(self, "attributes", AttributeHandler(self))
#_SA(self, "aliases", AliasHandler(self, category_prefix="script_"))
# 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

View file

@ -100,6 +100,7 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg
for field in update_fields: for field in update_fields:
fieldname = field.name fieldname = field.name
new_value = field.value_from_object(instance) new_value = field.value_from_object(instance)
# try to see if there is a handler on object that should be triggered when saving.
handlername = "_%s_handler" % fieldname handlername = "_%s_handler" % fieldname
try: try:
handler = _GA(instance, handlername) handler = _GA(instance, handlername)

View file

@ -50,7 +50,7 @@ class AttributeManager(models.Manager):
def exists(self,*args, **kwargs): def exists(self,*args, **kwargs):
return super(AttributeManager, self).exists(*args, **kwargs) return super(AttributeManager, self).exists(*args, **kwargs)
def get_attrs_on_obj(self, searchstr, obj, exact_match=True): def get_attrs_on_obj(self, searchstr, obj, category=None, exact_match=True):
""" """
Searches the object's attributes for attribute key matches. Searches the object's attributes for attribute key matches.
@ -58,10 +58,11 @@ class AttributeManager(models.Manager):
""" """
# Retrieve the list of attributes for this object. # Retrieve the list of attributes for this object.
category_cond = Q(db_category__iexact=category) if category else Q()
if exact_match: if exact_match:
return _GA("obj", "db_attributes").filter(db_key__iexact=searchstr) return _GA("obj", "db_attributes").filter(db_key__iexact=searchstr & category_cond)
else: else:
return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr) return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr & category_cond)
def attr_namesearch(self, *args, **kwargs): def attr_namesearch(self, *args, **kwargs):
"alias wrapper for backwards compatability" "alias wrapper for backwards compatability"
@ -86,62 +87,6 @@ class AttributeManager(models.Manager):
"alias wrapper for backwards compatability" "alias wrapper for backwards compatability"
return self.get_attr_by_value(self, *args, **kwargs) return self.get_attr_by_value(self, *args, **kwargs)
#
# LiteAttributeManager
#
class LiteAttributeManager(models.Manager):
"""
Manager methods for LiteAttributes
"""
def get_lattrs_on_obj(self, obj, search_key=None, category=None):
"""
Get all lattrs on obj, optionally limited by key and/or category
"""
if search_key or category:
key_cands = Q(db_key__iexact=search_key.lower().strip()) if search_key!=None else Q()
cat_cands = Q(db_category__iexact=category.lower.strip()) if search_key!=None else Q()
return _GA(obj, "db_liteattributes").filter(cat_cands & key_cands)
else:
return list(_GA(obj, "db_liteattributes").all())
def get_lattr(self, search_key=None, category=None):
"""
Search and return all liteattrs matching any combination of
the search criteria.
search_key (string) - the lattr identifier
category (string) - the lattr category
"""
key_cands = Q(db_key__iexact=search_key.lower().strip()) if search_key!=None else Q()
cat_cands = Q(db_category__iexact=category.lower.strip()) if search_key!=None else Q()
return list(self.filter(key_cands & cat_cands))
def get_lattr_data(self, obj=None, search_key=None, category=None):
"""
Retrieve data from found lattrs in an efficient way. Returns a list of data
matching the search criterions
"""
key_cands = Q(db_key__iexact=search_key.lower().strip()) if search_key!=None else Q()
cat_cands = Q(db_category__iexact=category.lower.strip()) if search_key!=None else Q()
if obj:
query = _GA(obj, "db_liteattributes").filter(key_cands & cat_cands).prefetch_related("db_data")
else:
query = self.filter(key_cands & cat_cands).prefetch_related("db_data")
return [q.db_data for q in query]
def create_lattr(self, key, category=None, data=None, obj=None):
"""
Create a LiteAttribute. This makes sure the create case-insensitive keys.
"""
lattr = self.objects.create(db_key=key.lower().strip(),
db_category=category.lower().strip() if category!=None else None,
db_data=str(data) if data!=None else None)
lattr.save()
if obj:
obj.db_liteattributes.add(lattr)
return lattr
# #
# TagManager # TagManager
# #

View file

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Attribute.db_strvalue'
db.add_column(u'typeclasses_attribute', 'db_strvalue',
self.gf('django.db.models.fields.TextField')(null=True, blank=True),
keep_default=False)
# Adding field 'Attribute.db_category'
db.add_column(u'typeclasses_attribute', 'db_category',
self.gf('django.db.models.fields.CharField')(db_index=True, max_length=128, null=True, blank=True),
keep_default=False)
# Adding index on 'Tag', fields ['db_category']
db.create_index(u'typeclasses_tag', ['db_category'])
# Adding index on 'Tag', fields ['db_key']
db.create_index(u'typeclasses_tag', ['db_key'])
def backwards(self, orm):
# Removing index on 'Tag', fields ['db_key']
db.delete_index(u'typeclasses_tag', ['db_key'])
# Removing index on 'Tag', fields ['db_category']
db.delete_index(u'typeclasses_tag', ['db_category'])
# Deleting field 'Attribute.db_strvalue'
db.delete_column(u'typeclasses_attribute', 'db_strvalue')
# Deleting field 'Attribute.db_category'
db.delete_column(u'typeclasses_attribute', 'db_category')
models = {
u'typeclasses.attribute': {
'Meta': {'object_name': 'Attribute'},
'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'typeclasses.liteattribute': {
'Meta': {'object_name': 'LiteAttribute', 'index_together': "(('db_key', 'db_category'),)"},
'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
'db_data': ('django.db.models.fields.TextField', [], {}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'typeclasses.tag': {
'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"},
'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}),
'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['typeclasses']

View file

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting model 'LiteAttribute'
db.delete_table(u'typeclasses_liteattribute')
def backwards(self, orm):
# Adding index on 'LiteAttribute', fields ['db_key', 'db_category']
db.create_index(u'typeclasses_liteattribute', ['db_key', 'db_category'])
# Adding model 'LiteAttribute'
db.create_table(u'typeclasses_liteattribute', (
('db_category', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)),
('db_key', self.gf('django.db.models.fields.CharField')(max_length=255)),
('db_data', self.gf('django.db.models.fields.TextField')()),
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal(u'typeclasses', ['LiteAttribute'])
models = {
u'typeclasses.attribute': {
'Meta': {'object_name': 'Attribute'},
'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'typeclasses.tag': {
'Meta': {'unique_together': "(('db_key', 'db_category'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category'),)"},
'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}),
'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['typeclasses']

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ 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.core.exceptions import ObjectDoesNotExist, FieldError
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, get_evennia_pids from src.utils.utils import dbref, get_evennia_pids
@ -83,7 +83,7 @@ class SharedMemoryModelBase(ModelBase):
document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise. document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise.
""" """
super(SharedMemoryModelBase, cls).__init__(*args, **kwargs) super(SharedMemoryModelBase, cls).__init__(*args, **kwargs)
def create_wrapper(cls, fieldname, wrappername): def create_wrapper(cls, fieldname, wrappername, editable=True):
"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"
@ -94,6 +94,9 @@ class SharedMemoryModelBase(ModelBase):
return _GA(value, "typeclass") return _GA(value, "typeclass")
return value return value
def _set_nonedit(cls, fname, value):
"Wrapper for blocking editing of field"
raise FieldError("Field %s cannot be edited." % fname)
def _set(cls, fname, value): def _set(cls, fname, value):
"Wrapper for setting database field" "Wrapper for setting database field"
#print "_set:", fname #print "_set:", fname
@ -108,7 +111,7 @@ class SharedMemoryModelBase(ModelBase):
try: try:
value = cls._default_manager.get(id=dbid) value = cls._default_manager.get(id=dbid)
except ObjectDoesNotExist: except ObjectDoesNotExist:
# maybe it is just a name # maybe it is just a name that happens to look like a dbid
pass pass
#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)
@ -117,6 +120,9 @@ class SharedMemoryModelBase(ModelBase):
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_nonedit(cls, fname):
"wrapper for not allowing deletion"
raise FieldError("Field %s cannot be edited." % fname)
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)
@ -125,8 +131,8 @@ class SharedMemoryModelBase(ModelBase):
# 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) if editable else _set_nonedit(cls, fieldname, val)
fdel = lambda cls: _del(cls, fieldname) fdel = lambda cls: _del(cls, fieldname) if editable else _del_nonedit(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))
@ -138,11 +144,11 @@ class SharedMemoryModelBase(ModelBase):
fieldname = field.name fieldname = field.name
if not fieldname.startswith("db_"): if not fieldname.startswith("db_"):
continue continue
wrappername = fieldname == "id" and "dbid" or fieldname.replace("db_", "") wrappername = "dbid" if fieldname == "id" else fieldname.replace("db_", "")
if not hasattr(cls, wrappername): if not hasattr(cls, wrappername):
# makes 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, editable=field.editable)
class SharedMemoryModel(Model): class SharedMemoryModel(Model):
# CL: setting abstract correctly to allow subclasses to inherit the default # CL: setting abstract correctly to allow subclasses to inherit the default