schema.py
changeset 2126 a25859917ccc
parent 1977 606923dff11b
child 2128 464edb198faa
equal deleted inserted replaced
2123:3e1d2ab5f8c0 2126:a25859917ccc
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
       
     9 _ = unicode
     9 
    10 
    10 import re
    11 import re
    11 from logging import getLogger
    12 from logging import getLogger
    12 from warnings import warn
    13 from warnings import warn
    13 
    14 
    28 # XXX <3.2 bw compat
    29 # XXX <3.2 bw compat
    29 from yams import schema
    30 from yams import schema
    30 schema.use_py_datetime()
    31 schema.use_py_datetime()
    31 nodes.use_py_datetime()
    32 nodes.use_py_datetime()
    32 
    33 
    33 _ = unicode
    34 #  set of meta-relations available for every entity types
    34 
    35 META_RELATIONS_TYPES = set((
    35 BASEGROUPS = ('managers', 'users', 'guests', 'owners')
    36     'owned_by', 'created_by', 'is', 'is_instance_of', 'identity',
    36 
    37     'eid', 'creation_date', 'modification_date', 'has_text',
    37 LOGGER = getLogger('cubicweb.schemaloader')
    38     )))
       
    39 
       
    40 #  set of entity and relation types used to build the schema
       
    41 SCHEMA_TYPES = set((
       
    42     'CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
       
    43     'CWConstraint', 'CWConstraintType', 'RQLExpression',
       
    44     'relation_type', 'from_entity', 'to_entity',
       
    45     'constrained_by', 'cstrtype',
       
    46     # XXX those are not really "schema" entity types
       
    47     #     but we usually don't want them as @* targets
       
    48     'CWProperty', 'CWPermission', 'State', 'Transition',
       
    49     ))
       
    50 
       
    51 _LOGGER = getLogger('cubicweb.schemaloader')
    38 
    52 
    39 # schema entities created from serialized schema have an eid rproperty
    53 # schema entities created from serialized schema have an eid rproperty
    40 ybo.ETYPE_PROPERTIES += ('eid',)
    54 ybo.ETYPE_PROPERTIES += ('eid',)
    41 ybo.RTYPE_PROPERTIES += ('eid',)
    55 ybo.RTYPE_PROPERTIES += ('eid',)
    42 ybo.RDEF_PROPERTIES += ('eid',)
    56 ybo.RDEF_PROPERTIES += ('eid',)
    66     if '*' in etype or '@' in etype:
    80     if '*' in etype or '@' in etype:
    67         assert len(etype) in (1, 2)
    81         assert len(etype) in (1, 2)
    68         etypes = ()
    82         etypes = ()
    69         if '*' in etype:
    83         if '*' in etype:
    70             etypes += tuple(self._wildcard_etypes(schema))
    84             etypes += tuple(self._wildcard_etypes(schema))
       
    85         # XXX deprecate, too clumsy
    71         if '@' in etype:
    86         if '@' in etype:
    72             etypes += tuple(system_etypes(schema))
    87             etypes += tuple(system_etypes(schema))
    73         return etypes
    88         return etypes
    74     return (etype,)
    89     return (etype,)
    75 ybo.RelationDefinition._actual_types = _actual_types
    90 ybo.RelationDefinition._actual_types = _actual_types
   240 
   255 
   241 def system_etypes(schema):
   256 def system_etypes(schema):
   242     """return system entity types only: skip final, schema and application entities
   257     """return system entity types only: skip final, schema and application entities
   243     """
   258     """
   244     for eschema in schema.entities():
   259     for eschema in schema.entities():
   245         if eschema.is_final() or eschema.schema_entity() or not eschema.meta:
   260         if eschema.is_final() or eschema.schema_entity():
   246             continue
   261             continue
   247         yield eschema.type
   262         yield eschema.type
   248 
   263 
   249 # Schema objects definition ###################################################
   264 # Schema objects definition ###################################################
   250 
   265 
   319         elif not need_has_text and has_has_text:
   334         elif not need_has_text and has_has_text:
   320             self.schema.del_relation_def(self.type, 'has_text', 'String')
   335             self.schema.del_relation_def(self.type, 'has_text', 'String')
   321 
   336 
   322     def schema_entity(self):
   337     def schema_entity(self):
   323         """return True if this entity type is used to build the schema"""
   338         """return True if this entity type is used to build the schema"""
   324         return self.type in self.schema.schema_entity_types()
   339         return self.type in SCHEMA_TYPES
   325 
   340 
   326     def check_perm(self, session, action, eid=None):
   341     def check_perm(self, session, action, eid=None):
   327         # NB: session may be a server session or a request object
   342         # NB: session may be a server session or a request object
   328         user = session.user
   343         user = session.user
   329         # check user is in an allowed group, if so that's enough
   344         # check user is in an allowed group, if so that's enough
   356         super(CubicWebRelationSchema, self).__init__(schema, rdef, **kwargs)
   371         super(CubicWebRelationSchema, self).__init__(schema, rdef, **kwargs)
   357         if eid is None and rdef is not None:
   372         if eid is None and rdef is not None:
   358             eid = getattr(rdef, 'eid', None)
   373             eid = getattr(rdef, 'eid', None)
   359         self.eid = eid
   374         self.eid = eid
   360 
   375 
       
   376     @property
       
   377     def meta(self):
       
   378         return self.type in META_RELATIONS_TYPES
   361 
   379 
   362     def update(self, subjschema, objschema, rdef):
   380     def update(self, subjschema, objschema, rdef):
   363         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
   381         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
   364         if not self._perms_checked and self._groups:
   382         if not self._perms_checked and self._groups:
   365             for action, groups in self._groups.iteritems():
   383             for action, groups in self._groups.iteritems():
   394         card = self.rproperty(subjtype, objtype, 'cardinality')
   412         card = self.rproperty(subjtype, objtype, 'cardinality')
   395         return (target == 'subject' and card[0]) or \
   413         return (target == 'subject' and card[0]) or \
   396                (target == 'object' and card[1])
   414                (target == 'object' and card[1])
   397 
   415 
   398     def schema_relation(self):
   416     def schema_relation(self):
   399         return self.type in ('relation_type', 'from_entity', 'to_entity',
   417         """return True if this relation type is used to build the schema"""
   400                              'constrained_by', 'cstrtype')
   418         return self.type in SCHEMA_TYPES
   401 
   419 
   402     def physical_mode(self):
   420     def physical_mode(self):
   403         """return an appropriate mode for physical storage of this relation type:
   421         """return an appropriate mode for physical storage of this relation type:
   404         * 'subjectinline' if every possible subject cardinalities are 1 or ?
   422         * 'subjectinline' if every possible subject cardinalities are 1 or ?
   405         * 'objectinline' if 'subjectinline' mode is not possible but every
   423         * 'objectinline' if 'subjectinline' mode is not possible but every
   454         rschema.final = True
   472         rschema.final = True
   455         rschema.set_default_groups()
   473         rschema.set_default_groups()
   456         rschema = self.add_relation_type(ybo.RelationType('identity', meta=True))
   474         rschema = self.add_relation_type(ybo.RelationType('identity', meta=True))
   457         rschema.final = False
   475         rschema.final = False
   458         rschema.set_default_groups()
   476         rschema.set_default_groups()
   459 
       
   460     def schema_entity_types(self):
       
   461         """return the list of entity types used to build the schema"""
       
   462         return frozenset(('CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
       
   463                           'CWConstraint', 'CWConstraintType', 'RQLExpression',
       
   464                           # XXX those are not really "schema" entity types
       
   465                           #     but we usually don't want them as @* targets
       
   466                           'CWProperty', 'CWPermission', 'State', 'Transition'))
       
   467 
   477 
   468     def add_entity_type(self, edef):
   478     def add_entity_type(self, edef):
   469         edef.name = edef.name.encode()
   479         edef.name = edef.name.encode()
   470         edef.name = bw_normalize_etype(edef.name)
   480         edef.name = bw_normalize_etype(edef.name)
   471         assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
   481         assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
   634             self.rqlst = parse(self.full_rql, print_errors=False).children[0]
   644             self.rqlst = parse(self.full_rql, print_errors=False).children[0]
   635         except RQLSyntaxError:
   645         except RQLSyntaxError:
   636             raise RQLSyntaxError(expression)
   646             raise RQLSyntaxError(expression)
   637         for mainvar in mainvars.split(','):
   647         for mainvar in mainvars.split(','):
   638             if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
   648             if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
   639                 LOGGER.warn('You did not use the %s variable in your RQL expression %s',
   649                 _LOGGER.warn('You did not use the %s variable in your RQL '
   640                             mainvar, self)
   650                              'expression %s', mainvar, self)
   641 
   651 
   642     def __str__(self):
   652     def __str__(self):
   643         return self.full_rql
   653         return self.full_rql
   644     def __repr__(self):
   654     def __repr__(self):
   645         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
   655         return '%s(%s)' % (self.__class__.__name__, self.full_rql)