First implementation of new Tags and LiteAttribute models to replace Aliases and Nicks while offering more flexibility.
This commit is contained in:
parent
e2ce3f2223
commit
e178963941
2 changed files with 233 additions and 7 deletions
|
|
@ -5,7 +5,7 @@ all Attributes and TypedObjects).
|
||||||
"""
|
"""
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.db.models import Q
|
||||||
from src.utils import idmapper
|
from src.utils import idmapper
|
||||||
from src.utils.utils import make_iter
|
from src.utils.utils import make_iter
|
||||||
from src.utils.dbserialize import to_pickle
|
from src.utils.dbserialize import to_pickle
|
||||||
|
|
@ -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 attr_namesearch(self, searchstr, obj, exact_match=True):
|
def get_attrs_on_obj(self, searchstr, obj, exact_match=True):
|
||||||
"""
|
"""
|
||||||
Searches the object's attributes for attribute key matches.
|
Searches the object's attributes for attribute key matches.
|
||||||
|
|
||||||
|
|
@ -63,7 +63,11 @@ class AttributeManager(models.Manager):
|
||||||
else:
|
else:
|
||||||
return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr)
|
return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr)
|
||||||
|
|
||||||
def attr_valuesearch(self, searchstr, obj=None):
|
def attr_namesearch(self, *args, **kwargs):
|
||||||
|
"alias wrapper for backwards compatability"
|
||||||
|
return self.get_attrs_on_obj(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_attr_by_value(self, searchstr, obj=None):
|
||||||
"""
|
"""
|
||||||
Searches obj for Attributes with a given value.
|
Searches obj for Attributes with a given value.
|
||||||
searchstr - value to search for. This may be any suitable object.
|
searchstr - value to search for. This may be any suitable object.
|
||||||
|
|
@ -78,6 +82,128 @@ class AttributeManager(models.Manager):
|
||||||
return _GA(obj, "db_attributes").filter(db_value=searchstr)
|
return _GA(obj, "db_attributes").filter(db_value=searchstr)
|
||||||
return self.filter(db_value=searchstr)
|
return self.filter(db_value=searchstr)
|
||||||
|
|
||||||
|
def attr_valuesearch(self, *args, **kwargs):
|
||||||
|
"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_tags").filter(cat_cands & key_cands)
|
||||||
|
else:
|
||||||
|
return list(_GA(obj, "db_tags").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
|
||||||
|
#
|
||||||
|
|
||||||
|
class TagManager(models.Manager):
|
||||||
|
"""
|
||||||
|
Extra manager methods for Tags
|
||||||
|
"""
|
||||||
|
def get_tags_on_obj(self, obj, search_key=None, category=None):
|
||||||
|
"""
|
||||||
|
Get all tags 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_tags").filter(cat_cands & key_cands)
|
||||||
|
else:
|
||||||
|
return list(_GA(obj, "db_tags").all())
|
||||||
|
|
||||||
|
def get_tag(self, search_key=None, category=None):
|
||||||
|
"""
|
||||||
|
Search and return all tags matching any combination of
|
||||||
|
the search criteria.
|
||||||
|
search_key (string) - the tag identifier
|
||||||
|
category (string) - the tag 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_objs_with_tag(self, objclass, search_key=None, category=None):
|
||||||
|
"""
|
||||||
|
Search and return all objects of objclass that has tags matching
|
||||||
|
the given search criteria.
|
||||||
|
objclass (dbmodel) - the object class to search
|
||||||
|
search_key (string) - the tag identifier
|
||||||
|
category (string) - the tag category
|
||||||
|
"""
|
||||||
|
key_cands = Q(db_tags__db_key__iexact=search_key.lower().strip()) if search_key!=None else Q()
|
||||||
|
cat_cands = Q(db_tags__db_category__iexact=category.lower().strip()) if category!=None else Q()
|
||||||
|
return objclass.objects.filter(key_cands & cat_cands)
|
||||||
|
|
||||||
|
def create_tag(self, key=None, category=None, data=None):
|
||||||
|
"""
|
||||||
|
Create a tag. This makes sure the create case-insensitive tags.
|
||||||
|
Note that if the exact same tag configuration (key+category)
|
||||||
|
exists, it will be re-used. A data keyword will overwrite existing
|
||||||
|
data on a tag (it is not part of what makes the tag unique).
|
||||||
|
|
||||||
|
"""
|
||||||
|
data = str(data) if data!=None else None
|
||||||
|
|
||||||
|
tag = self.get_tag(search_key=key, search_category=category)
|
||||||
|
if tag and data != None:
|
||||||
|
tag.db_data = data
|
||||||
|
tag.save()
|
||||||
|
elif not tag:
|
||||||
|
tag = self.objects.create(db_key=key.lower().strip() if key!=None else None,
|
||||||
|
db_category=category.lower().strip() if key!=None else None,
|
||||||
|
db_data=str(data) if data!=None else None)
|
||||||
|
tag.save()
|
||||||
|
return tag
|
||||||
|
|
||||||
#
|
#
|
||||||
# helper functions for the TypedObjectManager.
|
# helper functions for the TypedObjectManager.
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
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_attr_cache, set_attr_cache
|
from src.server.caches import get_attr_cache, set_attr_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, flush_attr_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, flush_attr_cache
|
||||||
|
|
||||||
from django.db.models.signals import m2m_changed
|
from django.db.models.signals import m2m_changed
|
||||||
from src.server.caches import post_attr_update
|
from src.server.caches import post_attr_update
|
||||||
|
|
@ -51,7 +51,7 @@ from src.server.models import ServerConfig
|
||||||
from src.typeclasses import managers
|
from src.typeclasses import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.utils.utils import make_iter, is_iter, to_unicode, to_str
|
from src.utils.utils import make_iter, is_iter, to_str
|
||||||
from src.utils.dbserialize import to_pickle, from_pickle
|
from src.utils.dbserialize import to_pickle, from_pickle
|
||||||
from src.utils.picklefield import PickledObjectField
|
from src.utils.picklefield import PickledObjectField
|
||||||
|
|
||||||
|
|
@ -252,6 +252,101 @@ class Attribute(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# LiteAttributes
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
class LiteAttribute(models.Model):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This specialized model is a middle-road between a Tag
|
||||||
|
and an Attribute. A LiteAttribute is smaller, less complex
|
||||||
|
to search than an Attribute (its key is indexed together with its
|
||||||
|
category) but can only hold strings in its db_value property whereas
|
||||||
|
an Attribute can hold arbitrary data. A LiteAttribute is also not
|
||||||
|
kept in memory in the same way as Attributes and has no inherent
|
||||||
|
concept of access restrictions which makes it unsuitable for modification
|
||||||
|
by untrusted users.
|
||||||
|
|
||||||
|
The difference between Liteattrs and Tags are that liteattrs are
|
||||||
|
not shared/unique but are created separately for each object holding them.
|
||||||
|
|
||||||
|
LiteAttributes are accessed through the db_liteattributes many2many field on
|
||||||
|
Typed Objects.
|
||||||
|
|
||||||
|
|
||||||
|
The main default use of the LiteAttribute is to implement Nick replacement.
|
||||||
|
In this case the category determines when the replacement is to be checked.
|
||||||
|
The key value is the input to replace and the data value holds the replacement
|
||||||
|
text.
|
||||||
|
|
||||||
|
The default nick category types used by Evennia are:
|
||||||
|
nick_inputline (default) - match against all input
|
||||||
|
nick_player - match against player searches
|
||||||
|
nick_obj - match against object searches
|
||||||
|
nick_channel - used to store own names for channels
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_key = models.CharField('key', max_length=255, help_text='name if liteattribute')
|
||||||
|
db_category = models.CharField('category', max_length=32, null=True, blank=True, help_text="liteattribute category")
|
||||||
|
db_data = models.TextField('data', help_text='holds string data')
|
||||||
|
|
||||||
|
objects = managers.LiteAttributeManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"Define Django meta options"
|
||||||
|
abstract = True
|
||||||
|
verbose_name = "Lite Attribute"
|
||||||
|
index_together = ("db_key", "db_category")
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s" % self.db_key
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.db_key)
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Tags
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
class Tag(models.Model):
|
||||||
|
"""
|
||||||
|
Tags are quick markers for objects in-game. An typeobject
|
||||||
|
can have any number of tags, stored via its db_tags property.
|
||||||
|
Tagging similar objects will make it easier to quickly locate the
|
||||||
|
group later (such as when implementing zones). The main advantage
|
||||||
|
of tagging as opposed to using Attributes is speed; a tag is very
|
||||||
|
limited in what data it can hold, and the tag key+category is
|
||||||
|
indexed for efficient lookup in the database. Tags are shared between
|
||||||
|
objects - a new tag is only created if the key+category combination
|
||||||
|
did not previously exist, making them unsuitable for storing
|
||||||
|
object-related data (for this a LiteAttribute or a full Attribute
|
||||||
|
should be used).
|
||||||
|
The 'db_data' field is intended as a documentation
|
||||||
|
field for the tag itself, such as to document what this tag+category
|
||||||
|
stands for and display that in a web interface or similar.
|
||||||
|
|
||||||
|
The main default use for Tags is to implement Aliases for objects.
|
||||||
|
this uses the 'aliases' tag category, which is also checked by the
|
||||||
|
default search functions of Evennia to allow quick searches by alias.
|
||||||
|
"""
|
||||||
|
db_key = models.CharField('key', max_length=64, null=True, help_text="tag identifier")
|
||||||
|
db_category = models.CharField('category', max_length=32, null=True, help_text="tag category")
|
||||||
|
db_data = models.TextField('data', null=True, blank=True, help_text="optional data field with extra information. This is not searched for.")
|
||||||
|
|
||||||
|
objects = managers.TagManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"Define Django meta options"
|
||||||
|
verbose_name = "Tag"
|
||||||
|
unique_together = ('db_key', 'db_category')
|
||||||
|
index_together = ('db_key', 'db_category')
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s" % self.db_key
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.db_key)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -417,9 +512,14 @@ class TypedObject(SharedMemoryModel):
|
||||||
# Lock storage
|
# Lock storage
|
||||||
db_lock_storage = models.TextField('locks', blank=True,
|
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.")
|
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. This is accessed through the self.db handler.
|
# many2many relationships
|
||||||
db_attributes = models.ManyToManyField(Attribute, null=True,
|
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).')
|
help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).')
|
||||||
|
db_liteattributes = models.ManyToManyField(LiteAttribute, null=True,
|
||||||
|
help_text='liteattributes on this object. A LiteAttribute holds a key, a category and a string field for simple lookups.')
|
||||||
|
db_tags = models.ManyToManyField(Tag, null=True,
|
||||||
|
help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.')
|
||||||
|
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = managers.TypedObjectManager()
|
objects = managers.TypedObjectManager()
|
||||||
|
|
||||||
|
|
@ -1258,5 +1358,5 @@ class TypedObject(SharedMemoryModel):
|
||||||
self.__class__.flush_cached_instance(self)
|
self.__class__.flush_cached_instance(self)
|
||||||
|
|
||||||
|
|
||||||
# connect to attribut cache signal
|
# connect to attribute cache signal
|
||||||
m2m_changed.connect(post_attr_update, sender=TypedObject.db_attributes.through)
|
m2m_changed.connect(post_attr_update, sender=TypedObject.db_attributes.through)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue