--- a/entity.py Tue Aug 11 17:27:27 2009 +0200
+++ b/entity.py Wed Aug 12 18:05:19 2009 +0200
@@ -132,20 +132,14 @@
return super(_metaentity, mcs).__new__(mcs, name, bases, classdict)
-_CWDB_CLASSES = {}
-
-def cwdb___init__(self, entity):
- self.entity = entity
+class Entity(AppObject, dict):
+ """an entity instance has e_schema automagically set on
+ the class and instances has access to their issuing cursor.
-class Entity(AppObject, dict):
- """an entity instance has e_schema automagically set on the class.
-
- Also its special cwdb attribute provides access to persistent attributes and
- relation using properties which are set for each attribute and relation
- according to entity's type schema.
-
- Beware that among attributes, 'eid' is *NEITHER* stored in the dict
- containment (which acts as a cache for other attributes dynamically fetched)
+ A property is set for each attribute and relation on each entity's type
+ class. Becare that among attributes, 'eid' is *NEITHER* stored in the
+ dict containment (which acts as a cache for other attributes dynamically
+ fetched)
:type e_schema: `cubicweb.schema.EntitySchema`
:ivar e_schema: the entity's schema
@@ -183,30 +177,24 @@
etype = cls.id
assert etype != 'Any', etype
cls.e_schema = eschema = cls.schema.eschema(etype)
- cwdbclsdict = {'__init__': cwdb___init__}
for rschema, _ in eschema.attribute_definitions():
- rtype = rschema.type
- if rtype == 'eid':
+ if rschema.type == 'eid':
continue
- setattr(cls, rtype, DeprecatedAttribute(rtype))
- cwdbclsdict[rtype] = Attribute(rtype)
+ setattr(cls, rschema.type, Attribute(rschema.type))
mixins = []
- for rschema, _, role in eschema.relation_definitions():
- if (rschema, role) in MI_REL_TRIGGERS:
- mixin = MI_REL_TRIGGERS[(rschema, role)]
+ for rschema, _, x in eschema.relation_definitions():
+ if (rschema, x) in MI_REL_TRIGGERS:
+ mixin = MI_REL_TRIGGERS[(rschema, x)]
if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
mixins.append(mixin)
for iface in getattr(mixin, '__implements__', ()):
if not interface.implements(cls, iface):
interface.extend(cls, iface)
- rtype = rschema.type
- if role == 'object':
- attr = 'reverse_%s' % rtype
+ if x == 'subject':
+ setattr(cls, rschema.type, SubjectRelation(rschema))
else:
- attr = rtype
- setattr(cls, attr, DeprecatedRelation(rtype, role))
- cwdbclsdict[attr] = Relation(rtype, role)
- _CWDB_CLASSES[etype] = type(etype + 'CWDB', (object,), cwdbclsdict)
+ attr = 'reverse_%s' % rschema.type
+ setattr(cls, attr, ObjectRelation(rschema))
if mixins:
cls.__bases__ = tuple(mixins + [p for p in cls.__bases__ if not p is object])
cls.debug('plugged %s mixins on %s', mixins, etype)
@@ -311,8 +299,6 @@
else:
self.eid = None
self._is_saved = True
- if self.id != 'Any':
- self.cwdb = _CWDB_CLASSES[self.id](self)
def __repr__(self):
return '<Entity %s %s %s at %s>' % (
@@ -324,15 +310,6 @@
def __hash__(self):
return id(self)
- def cwgetattr(self, attr, default=_marker):
- """return attribute from either self.cwdb or self"""
- try:
- return getattr(self.cwdb, attr)
- except AttributeError:
- if default is _marker:
- return getattr(self, attr)
- return getattr(self, attr, default)
-
def pre_add_hook(self):
"""hook called by the repository before doing anything to add the entity
(before_add entity hooks have not been called yet). This give the
@@ -415,7 +392,7 @@
etype = str(self.e_schema)
path = etype.lower()
if mainattr != 'eid':
- value = getattr(self.cwdb, mainattr)
+ value = getattr(self, mainattr)
if value is None or unicode(value) == u'':
mainattr = 'eid'
path += '/eid'
@@ -436,7 +413,7 @@
def attr_metadata(self, attr, metadata):
"""return a metadata for an attribute (None if unspecified)"""
- value = self.cwgetattr('%s_%s' % (attr, metadata), None)
+ value = getattr(self, '%s_%s' % (attr, metadata), None)
if value is None and metadata == 'encoding':
value = self.vreg.property_value('ui.encoding')
return value
@@ -448,17 +425,14 @@
"""
attr = str(attr)
if value is _marker:
- value = self.cwgetattr(attr)
+ value = getattr(self, attr)
if isinstance(value, basestring):
value = value.strip()
if value is None or value == '': # don't use "not", 0 is an acceptable value
return u''
if attrtype is None:
attrtype = self.e_schema.destination(attr)
- try:
- props = self.e_schema.rproperties(attr)
- except KeyError:
- props = {}
+ props = self.e_schema.rproperties(attr)
if attrtype == 'String':
# internalinalized *and* formatted string such as schema
# description...
@@ -499,20 +473,13 @@
assert self.has_eid()
execute = self.req.execute
for rschema in self.e_schema.subject_relations():
- # skip final, meta or composite relation
- if rschema.is_final() or rschema.meta or self.e_schema.subjrproperty(rschema, 'composite'):
+ if rschema.is_final() or rschema.meta:
continue
- # skip relation with card in ?1 else we either change the copied
- # object (inlined relation) or inserting some inconsistency
- if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
+ # skip already defined relations
+ if getattr(self, rschema.type):
continue
- # skip if we're told to do so
if rschema.type in self.skip_copy_for:
continue
- # skip already defined relations
- if self.related(rschema.type, 'subject'):
- continue
- # special case for in_state
if rschema.type == 'in_state':
# if the workflow is defining an initial state (XXX AND we are
# not in the managers group? not done to be more consistent)
@@ -520,26 +487,32 @@
if execute('Any S WHERE S state_of ET, ET initial_state S,'
'ET name %(etype)s', {'etype': str(self.e_schema)}):
continue
+ # skip composite relation
+ if self.e_schema.subjrproperty(rschema, 'composite'):
+ continue
+ # skip relation with card in ?1 else we either change the copied
+ # object (inlined relation) or inserting some inconsistency
+ if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
+ continue
rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
- rschema, rschema)
+ rschema.type, rschema.type)
execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
self.clear_related_cache(rschema.type, 'subject')
for rschema in self.e_schema.object_relations():
- # skip meta or composite
- if rschema.meta or self.e_schema.objrproperty(rschema, 'composite'):
+ if rschema.meta:
+ continue
+ # skip already defined relations
+ if getattr(self, 'reverse_%s' % rschema.type):
+ continue
+ # skip composite relation
+ if self.e_schema.objrproperty(rschema, 'composite'):
continue
# skip relation with card in ?1 else we either change the copied
# object (inlined relation) or inserting some inconsistency
if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
continue
- # skip if we're told to do so
- if rschema.type in self.skip_copy_for:
- continue
- # skip already defined relations
- if self.related(rschema.type, 'object'):
- continue
rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
- rschema, rschema)
+ rschema.type, rschema.type)
execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
self.clear_related_cache(rschema.type, 'object')
@@ -904,9 +877,9 @@
yielded = False
for rschema, target in containers:
if target == 'object':
- targets = self.related(rschema.type, 'subject', entities=True)
+ targets = getattr(self, rschema.type)
else:
- targets = self.related(rschema.type, 'object', entities=True)
+ targets = getattr(self, 'reverse_%s' % rschema)
for entity in targets:
if entity.eid in _done:
continue
@@ -942,73 +915,61 @@
words += tokenize(value)
for rschema, role in self.e_schema.fulltext_relations():
- for entity in self.related(rschema.type, role, entities=True):
- words += entity.get_words()
+ if role == 'subject':
+ for entity in getattr(self, rschema.type):
+ words += entity.get_words()
+ else: # if role == 'object':
+ for entity in getattr(self, 'reverse_%s' % rschema.type):
+ words += entity.get_words()
return words
# attribute and relation descriptors ##########################################
-
class Attribute(object):
"""descriptor that controls schema attribute access"""
- def __init__(self, rtype):
- assert rtype != 'eid'
- self._rtype = rtype
+ def __init__(self, attrname):
+ assert attrname != 'eid'
+ self._attrname = attrname
+
+ def __get__(self, eobj, eclass):
+ if eobj is None:
+ return self
+ return eobj.get_value(self._attrname)
+
+ def __set__(self, eobj, value):
+ # XXX bw compat
+ # would be better to generate UPDATE queries than the current behaviour
+ eobj.warning("deprecated usage, don't use 'entity.attr = val' notation)")
+ eobj[self._attrname] = value
- def __get__(self, cwdbobj, eclass):
- if cwdbobj is None:
- return self
- return cwdbobj.entity.get_value(self._rtype)
+
+class Relation(object):
+ """descriptor that controls schema relation access"""
+ _role = None # for pylint
+
+ def __init__(self, rschema):
+ self._rschema = rschema
+ self._rtype = rschema.type
+
+ def __get__(self, eobj, eclass):
+ if eobj is None:
+ raise AttributeError('%s cannot be only be accessed from instances'
+ % self._rtype)
+ return eobj.related(self._rtype, self._role, entities=True)
def __set__(self, eobj, value):
raise NotImplementedError
-class Relation(Attribute):
+class SubjectRelation(Relation):
"""descriptor that controls schema relation access"""
-
- def __init__(self, rtype, role):
- super(Relation, self).__init__(rtype)
- self._role = role
-
- def __get__(self, cwdbobj, eclass):
- if cwdbobj is None:
- raise AttributeError('%s cannot be only be accessed from instances'
- % self._rtype)
- return cwdbobj.entity.related(self._rtype, self._role, entities=True)
-
-
-class DeprecatedAttribute(Attribute):
- """descriptor that controls schema attribute access"""
+ _role = 'subject'
- def __get__(self, eobj, eclass):
- rtype = self._rtype
- warn('entity.%s is deprecated, use entity.cwdb.%s' % (rtype, rtype),
- DeprecationWarning, stacklevel=2)
- if eobj is None:
- return self
- return eobj.get_value(self._rtype)
-
- def __set__(self, eobj, value):
- # XXX bw compat
- # would be better to generate UPDATE queries than the current behaviour
- eobj.warning("deprecated usage, don't use 'entity.attr = val' notation)")
- eobj[self._rtype] = value
-
-
-class DeprecatedRelation(Relation):
+class ObjectRelation(Relation):
"""descriptor that controls schema relation access"""
-
- def __get__(self, eobj, eclass):
- rtype = self._rtype
- warn('entity.[reverse_]%s is deprecated, use entity.cwdb.[reverse_]%s'
- % (rtype, rtype), DeprecationWarning, stacklevel=2)
- if eobj is None:
- raise AttributeError('%s cannot be only be accessed from instances'
- % self._rtype)
- return eobj.related(rtype, self._role, entities=True)
+ _role = 'object'
from logging import getLogger
from cubicweb import set_log_methods