entity.py
changeset 2827 d1a89d165045
parent 2824 3455f72010fe
child 2829 054a8805da52
equal deleted inserted replaced
2826:a4ddd807c5ee 2827:d1a89d165045
    10 from warnings import warn
    10 from warnings import warn
    11 
    11 
    12 from logilab.common import interface
    12 from logilab.common import interface
    13 from logilab.common.compat import all
    13 from logilab.common.compat import all
    14 from logilab.common.decorators import cached
    14 from logilab.common.decorators import cached
    15 from logilab.common.deprecation import deprecated
       
    16 from logilab.mtconverter import TransformData, TransformError, xml_escape
    15 from logilab.mtconverter import TransformData, TransformError, xml_escape
    17 
    16 
    18 from rql.utils import rqlvar_maker
    17 from rql.utils import rqlvar_maker
    19 
    18 
    20 from cubicweb import Unauthorized
    19 from cubicweb import Unauthorized
    21 from cubicweb.rset import ResultSet
    20 from cubicweb.rset import ResultSet
    22 from cubicweb.selectors import yes
    21 from cubicweb.selectors import yes
    23 from cubicweb.appobject import AppObject
    22 from cubicweb.appobject import AppObject
    24 from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype
    23 from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint
    25 
    24 
    26 from cubicweb.common.uilib import printable_value, soup2xhtml
    25 from cubicweb.common.uilib import printable_value, soup2xhtml
    27 from cubicweb.common.mixins import MI_REL_TRIGGERS
    26 from cubicweb.common.mixins import MI_REL_TRIGGERS
    28 from cubicweb.common.mttransforms import ENGINE
    27 from cubicweb.common.mttransforms import ENGINE
    29 
    28 
    36             if card in '+*':
    35             if card in '+*':
    37                 return card
    36                 return card
    38     return '1'
    37     return '1'
    39 
    38 
    40 
    39 
    41 _MODE_TAGS = set(('link', 'create'))
       
    42 _CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
       
    43 
       
    44 try:
       
    45     from cubicweb.web import formwidgets, uicfg
       
    46 
       
    47     def _dispatch_rtags(tags, rtype, role, stype, otype):
       
    48         for tag in tags:
       
    49             if tag in _MODE_TAGS:
       
    50                 uicfg.actionbox_appearsin_addmenu.tag_relation(
       
    51                     (stype, rtype, otype, role), tag == 'create')
       
    52             elif tag in _CATEGORY_TAGS:
       
    53                 uicfg.autoform_section.tag_relation((stype, rtype, otype, role),
       
    54                                                     tag)
       
    55             elif tag == 'inlineview':
       
    56                 uicfg.autoform_is_inlined.tag_relation((stype, rtype, otype, role), True)
       
    57             else:
       
    58                 raise ValueError(tag)
       
    59 
       
    60 except ImportError:
       
    61 
       
    62     _dispatch_rtags = None
       
    63 
       
    64 def _get_etype(bases, classdict):
       
    65     try:
       
    66         return classdict['id']
       
    67     except KeyError:
       
    68         for base in bases:
       
    69             etype = getattr(base, 'id', None)
       
    70             if etype and etype != 'Any':
       
    71                 return etype
       
    72 
       
    73 def _get_defs(attr, name, bases, classdict):
       
    74     try:
       
    75         yield name, classdict.pop(attr)
       
    76     except KeyError:
       
    77         for base in bases:
       
    78             try:
       
    79                 value = getattr(base, attr)
       
    80                 delattr(base, attr)
       
    81                 yield base.__name__, value
       
    82             except AttributeError:
       
    83                 continue
       
    84 
       
    85 class _metaentity(type):
       
    86     """this metaclass sets the relation tags on the entity class
       
    87     and deals with the `widgets` attribute
       
    88     """
       
    89     def __new__(mcs, name, bases, classdict):
       
    90         # collect baseclass' rtags
       
    91         etype = _get_etype(bases, classdict)
       
    92         if etype and _dispatch_rtags is not None:
       
    93             for name, rtags in _get_defs('__rtags__', name, bases, classdict):
       
    94                 warn('%s: __rtags__ is deprecated' % name, DeprecationWarning)
       
    95                 for relation, tags in rtags.iteritems():
       
    96                     # tags must become an iterable
       
    97                     if isinstance(tags, basestring):
       
    98                         tags = (tags,)
       
    99                     # relation must become a 3-uple (rtype, targettype, role)
       
   100                     if isinstance(relation, basestring):
       
   101                         _dispatch_rtags(tags, relation, 'subject', etype, '*')
       
   102                         _dispatch_rtags(tags, relation, 'object', '*', etype)
       
   103                     elif len(relation) == 1: # useful ?
       
   104                         _dispatch_rtags(tags, relation[0], 'subject', etype, '*')
       
   105                         _dispatch_rtags(tags, relation[0], 'object', '*', etype)
       
   106                     elif len(relation) == 2:
       
   107                         rtype, ttype = relation
       
   108                         ttype = bw_normalize_etype(ttype) # XXX bw compat
       
   109                         _dispatch_rtags(tags, rtype, 'subject', etype, ttype)
       
   110                         _dispatch_rtags(tags, rtype, 'object', ttype, etype)
       
   111                     elif len(relation) == 3:
       
   112                         rtype, ttype, role = relation
       
   113                         ttype = bw_normalize_etype(ttype)
       
   114                         if role == 'subject':
       
   115                             _dispatch_rtags(tags, rtype, 'subject', etype, ttype)
       
   116                         else:
       
   117                             _dispatch_rtags(tags, rtype, 'object', ttype, etype)
       
   118                     else:
       
   119                         raise ValueError('bad rtag definition (%r)' % (relation,))
       
   120             for name, widgets in _get_defs('widgets', name, bases, classdict):
       
   121                 warn('%s: widgets is deprecated' % name, DeprecationWarning)
       
   122                 for rtype, wdgname in widgets.iteritems():
       
   123                     if wdgname in ('URLWidget', 'EmbededURLWidget', 'RawDynamicComboBoxWidget'):
       
   124                         warn('%s widget is deprecated' % wdgname, DeprecationWarning)
       
   125                         continue
       
   126                     if wdgname == 'StringWidget':
       
   127                         wdgname = 'TextInput'
       
   128                     widget = getattr(formwidgets, wdgname)
       
   129                     assert hasattr(widget, 'render')
       
   130                     uicfg.autoform_field_kwargs.tag_subject_of(
       
   131                         (etype, rtype, '*'), {'widget': widget})
       
   132         return super(_metaentity, mcs).__new__(mcs, name, bases, classdict)
       
   133 
       
   134 
       
   135 class Entity(AppObject, dict):
    40 class Entity(AppObject, dict):
   136     """an entity instance has e_schema automagically set on
    41     """an entity instance has e_schema automagically set on
   137     the class and instances has access to their issuing cursor.
    42     the class and instances has access to their issuing cursor.
   138 
    43 
   139     A property is set for each attribute and relation on each entity's type
    44     A property is set for each attribute and relation on each entity's type
   153     :cvar skip_copy_for: a list of relations that should be skipped when copying
    58     :cvar skip_copy_for: a list of relations that should be skipped when copying
   154                          this kind of entity. Note that some relations such
    59                          this kind of entity. Note that some relations such
   155                          as composite relations or relations that have '?1' as object
    60                          as composite relations or relations that have '?1' as object
   156                          cardinality
    61                          cardinality
   157     """
    62     """
   158     __metaclass__ = _metaentity
       
   159     __registry__ = 'etypes'
    63     __registry__ = 'etypes'
   160     __select__ = yes()
    64     __select__ = yes()
   161 
    65 
   162     # class attributes that must be set in class definition
    66     # class attributes that must be set in class definition
   163     id = None
    67     id = None
   165     fetch_attrs = None
    69     fetch_attrs = None
   166     skip_copy_for = ()
    70     skip_copy_for = ()
   167     # class attributes set automatically at registration time
    71     # class attributes set automatically at registration time
   168     e_schema = None
    72     e_schema = None
   169 
    73 
   170     MODE_TAGS = set(('link', 'create'))
       
   171     CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
       
   172     @classmethod
    74     @classmethod
   173     def __initialize__(cls, schema):
    75     def __initialize__(cls, schema):
   174         """initialize a specific entity class by adding descriptors to access
    76         """initialize a specific entity class by adding descriptors to access
   175         entity type's attributes and relations
    77         entity type's attributes and relations
   176         """
    78         """