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.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_prop_cache, set_prop_cache
@ -140,9 +140,10 @@ class ObjectDB(TypedObject):
_SA(self, "cmdset", CmdSetHandler(self))
_GA(self, "cmdset").update(init_mode=True)
_SA(self, "scripts", ScriptHandler(self))
_SA(self, "tags", TagHandler(self, category_prefix="object_"))
_SA(self, "aliases", AliasHandler(self, category_prefix="object_"))
_SA(self, "nicks", NickHandler(self, category_prefix="object_"))
_SA(self, "attributes", AttributeHandler(self))
_SA(self, "tags", TagHandler(self))
_SA(self, "aliases", AliasHandler(self))
_SA(self, "nicks", NickHandler(self))
# make sure to sync the contents cache when initializing
self.contents_update()
@ -646,10 +647,10 @@ class ObjectDB(TypedObject):
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]]
# 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:
pnicks = self.player.db_liteattributes.filter(
db_category__in=("player_nick_inputline", "player_nick_channel")).prefetch_related("db_key","db_data")
pnicks = self.player.db_attributes.filter(
db_category__in=("nick_inputline", "nick_channel")).prefetch_related("db_key","db_strvalue")
nicks = list(nicks) + list(pnicks)
for nick in nicks:
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.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 import cmdhandler
from src.utils import utils
@ -113,9 +113,10 @@ class PlayerDB(TypedObject, AbstractUser):
# handlers
_SA(self, "cmdset", CmdSetHandler(self))
_GA(self, "cmdset").update(init_mode=True)
_SA(self, "attributes", AttributeHandler(self))
_SA(self, "tags", TagHandler(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
# @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 = [" ".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
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")
for nick in nicks:
if nick.db_key in raw_list:
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.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 src.scripts.manager import ScriptManager
@ -108,7 +108,8 @@ class ScriptDB(TypedObject):
def __init__(self, *args, **kwargs):
super(ScriptDB, self).__init__(*args, **kwargs)
_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
# @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:
fieldname = field.name
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
try:
handler = _GA(instance, handlername)

View file

@ -50,7 +50,7 @@ class AttributeManager(models.Manager):
def exists(self,*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.
@ -58,10 +58,11 @@ class AttributeManager(models.Manager):
"""
# Retrieve the list of attributes for this object.
category_cond = Q(db_category__iexact=category) if category else Q()
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:
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):
"alias wrapper for backwards compatability"
@ -86,62 +87,6 @@ class AttributeManager(models.Manager):
"alias wrapper for backwards compatability"
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
#

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.threads import blockingCallFromThread
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.signals import post_save, pre_delete, post_syncdb
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.
"""
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)"
def _get(cls, fname):
"Wrapper for getting database field"
@ -94,6 +94,9 @@ class SharedMemoryModelBase(ModelBase):
return _GA(value, "typeclass")
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):
"Wrapper for setting database field"
#print "_set:", fname
@ -108,7 +111,7 @@ class SharedMemoryModelBase(ModelBase):
try:
value = cls._default_manager.get(id=dbid)
except ObjectDoesNotExist:
# maybe it is just a name
# maybe it is just a name that happens to look like a dbid
pass
#print "_set wrapper:", fname, value, type(value), cls._get_pk_val(cls._meta)
_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
_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):
"Wrapper for clearing database field - sets it to None"
_SA(cls, fname, None)
@ -125,8 +131,8 @@ class SharedMemoryModelBase(ModelBase):
# create class wrappers
fget = lambda cls: _get(cls, fieldname)
fset = lambda cls, val: _set(cls, fieldname, val)
fdel = lambda cls: _del(cls, fieldname)
fset = lambda cls, val: _set(cls, fieldname, val) if editable else _set_nonedit(cls, fieldname, val)
fdel = lambda cls: _del(cls, fieldname) if editable else _del_nonedit(cls,fieldname)
doc = "Wraps setting, saving and deleting the %s field." % fieldname
type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel, doc))
@ -138,11 +144,11 @@ class SharedMemoryModelBase(ModelBase):
fieldname = field.name
if not fieldname.startswith("db_"):
continue
wrappername = fieldname == "id" and "dbid" or fieldname.replace("db_", "")
wrappername = "dbid" if fieldname == "id" else fieldname.replace("db_", "")
if not hasattr(cls, wrappername):
# makes sure not to overload manually created wrappers on the model
#print "wrapping %s -> %s" % (fieldname, wrappername)
create_wrapper(cls, fieldname, wrappername)
create_wrapper(cls, fieldname, wrappername, editable=field.editable)
class SharedMemoryModel(Model):
# CL: setting abstract correctly to allow subclasses to inherit the default