evennia/src/typeclasses/managers.py

196 lines
6.8 KiB
Python

"""
This implements the common managers that are used by the
abstract models in dbobjects.py (and which are thus shared by
all Attributes and TypedObjects).
"""
from functools import update_wrapper
from django.db import models
from django.contrib.contenttypes.models import ContentType
from src.utils import idmapper
from src.utils.utils import make_iter
from src.utils.dbserialize import to_pickle
__all__ = ("AttributeManager", "TypedObjectManager")
_GA = object.__getattribute__
# Managers
def _attr_pickled(method):
"""
decorator for safely handling attribute searches
- db_value is a pickled field and this is required
in order to be able for pickled django objects directly.
"""
def wrapper(self, *args, **kwargs):
"wrap all queries searching the db_value field in some way"
self.__doc__ = method.__doc__
for key in (key for key in kwargs if key.startswith('db_value')):
kwargs[key] = to_pickle(kwargs[key])
return method(self, *args, **kwargs)
return update_wrapper(wrapper, method)
class AttributeManager(models.Manager):
"Manager for handling Attributes."
@_attr_pickled
def get(self, *args, **kwargs):
return super(AttributeManager, self).get(*args, **kwargs)
@_attr_pickled
def filter(self,*args, **kwargs):
return super(AttributeManager, self).filter(*args, **kwargs)
@_attr_pickled
def exclude(self,*args, **kwargs):
return super(AttributeManager, self).exclude(*args, **kwargs)
@_attr_pickled
def values(self,*args, **kwargs):
return super(AttributeManager, self).values(*args, **kwargs)
@_attr_pickled
def values_list(self,*args, **kwargs):
return super(AttributeManager, self).values_list(*args, **kwargs)
@_attr_pickled
def exists(self,*args, **kwargs):
return super(AttributeManager, self).exists(*args, **kwargs)
def attr_namesearch(self, searchstr, obj, exact_match=True):
"""
Searches the object's attributes for attribute key matches.
searchstr: (str) A string to search for.
"""
# Retrieve the list of attributes for this object.
if exact_match:
return _GA("obj", "db_attributes").filter(db_key__iexact=searchstr)
else:
return _GA("obj", "db_attributes").filter(db_key__icontains=searchstr)
def attr_valuesearch(self, searchstr, obj=None):
"""
Searches obj for Attributes with a given value.
searchstr - value to search for. This may be any suitable object.
obj - limit to a given object instance
If no restraint is given, all Attributes on all types of objects
will be searched. It's highly recommended to at least
supply the objclass argument (DBObject, DBScript or DBPlayer)
to restrict this lookup.
"""
if obj:
return _GA(obj, "db_attributes").filter(db_value=searchstr)
return self.filter(db_value=searchstr)
#
# helper functions for the TypedObjectManager.
#
def returns_typeclass_list(method):
"""
Decorator: Changes return of the decorated method (which are
TypeClassed objects) into object_classes(s) instead. Will always
return a list (may be empty).
"""
def func(self, *args, **kwargs):
"decorator. Returns a list."
self.__doc__ = method.__doc__
matches = method(self, *args, **kwargs)
return [(hasattr(dbobj, "typeclass") and dbobj.typeclass) or dbobj for dbobj in make_iter(matches)]
return update_wrapper(func, method)
def returns_typeclass(method):
"""
Decorator: Will always return a single typeclassed result or None.
"""
def func(self, *args, **kwargs):
"decorator. Returns result or None."
self.__doc__ = method.__doc__
matches = method(self, *args, **kwargs)
dbobj = matches and make_iter(matches)[0] or None
if dbobj:
return (hasattr(dbobj, "typeclass") and dbobj.typeclass) or dbobj
return None
return update_wrapper(func, method)
#class TypedObjectManager(idmap.CachingManager):
#class TypedObjectManager(models.Manager):
class TypedObjectManager(idmapper.manager.SharedMemoryManager):
"""
Common ObjectManager for all dbobjects.
"""
def dbref(self, dbref, reqhash=True):
"""
Valid forms of dbref (database reference number)
are either a string '#N' or an integer N.
Output is the integer part.
reqhash - require input to be on form "#N" to be
identified as a dbref
"""
if reqhash and not (isinstance(dbref, basestring) and dbref.startswith("#")):
return None
if isinstance(dbref, basestring):
dbref = dbref.lstrip('#')
try:
if int(dbref) < 0:
return None
except Exception:
return None
return dbref
@returns_typeclass
def get_id(self, dbref):
"""
Find object with given dbref
"""
dbref = self.dbref(dbref, reqhash=False)
try:
return self.get(id=dbref)
except self.model.DoesNotExist:
pass
return None
def dbref_search(self, dbref):
"""
Alias to get_id
"""
return self.get_id(dbref)
@returns_typeclass_list
def get_dbref_range(self, min_dbref=None, max_dbref=None):
"""
Return all objects inside and including the
given boundaries.
"""
min_dbref, max_dbref = self.dbref(min_dbref), self.dbref(max_dbref)
if not min_dbref or not max_dbref:
return self.all()
if not min_dbref:
return self.filter(id__lte=max_dbref)
elif not max_dbref:
return self.filter(id__gte=min_dbref)
return self.filter(id__gte=min_dbref).filter(id__lte=min_dbref)
def object_totals(self):
"""
Returns a dictionary with all the typeclasses active in-game
as well as the number of such objects defined (i.e. the number
of database object having that typeclass set on themselves).
"""
dbtotals = {}
typeclass_paths = set(self.values_list('db_typeclass_path', flat=True))
for typeclass_path in typeclass_paths:
dbtotals[typeclass_path] = \
self.filter(db_typeclass_path=typeclass_path).count()
return dbtotals
@returns_typeclass_list
def typeclass_search(self, typeclass):
"""
Searches through all objects returning those which has a certain
typeclass. If location is set, limit search to objects in
that location.
"""
if callable(typeclass):
cls = typeclass.__class__
typeclass = "%s.%s" % (cls.__module__, cls.__name__)
return self.filter(db_typeclass_path__exact=typeclass)