doc/book/devrepo/datamodel/definition.rst
changeset 10491 c67bcee93248
parent 10301 729f36a1bcfa
child 10496 e95b559a06a2
equal deleted inserted replaced
10490:76ab3c71aff2 10491:c67bcee93248
       
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 .. _datamodel_definition:
       
     4 
       
     5 Yams *schema*
       
     6 -------------
       
     7 
       
     8 The **schema** is the core piece of a *CubicWeb* instance as it
       
     9 defines and handles the data model. It is based on entity types that
       
    10 are either already defined in `Yams`_ and the *CubicWeb* standard
       
    11 library; or more specific types defined in cubes. The schema for a
       
    12 cube is defined in a `schema` python module or package.
       
    13 
       
    14 .. _`Yams`: http://www.logilab.org/project/yams
       
    15 
       
    16 .. _datamodel_overview:
       
    17 
       
    18 Overview
       
    19 ~~~~~~~~
       
    20 
       
    21 The core idea of the yams schema is not far from the classical
       
    22 `Entity-relationship`_ model. But while an E/R model (or `logical
       
    23 model`) traditionally has to be manually translated to a lower-level
       
    24 data description language (such as the SQL `create table`
       
    25 sublanguage), also often described as the `physical model`, no such
       
    26 step is required with |yams| and |cubicweb|.
       
    27 
       
    28 .. _`Entity-relationship`: http://en.wikipedia.org/wiki/Entity-relationship_model
       
    29 
       
    30 This is because in addition to high-level, logical |yams| models, one
       
    31 uses the |rql| data manipulation language to query, insert, update and
       
    32 delete data. |rql| abstracts as much of the underlying SQL database as
       
    33 a |yams| schema abstracts from the physical layout. The vagaries of
       
    34 SQL are avoided.
       
    35 
       
    36 As a bonus point, such abstraction make it quite comfortable to build
       
    37 or use different backends to which |rql| queries apply.
       
    38 
       
    39 So, as in the E/R formalism, the building blocks are ``entities``
       
    40 (:ref:`EntityType`), ``relationships`` (:ref:`RelationType`,
       
    41 :ref:`RelationDefinition`) and ``attributes`` (handled like relation
       
    42 with |yams|).
       
    43 
       
    44 Let us detail a little the divergences between E/R and |yams|:
       
    45 
       
    46 * all relationship are binary which means that to represent a
       
    47   non-binary relationship, one has to use an entity,
       
    48 * relationships do not support attributes (yet, see:
       
    49   http://www.cubicweb.org/ticket/341318), hence the need to reify it
       
    50   as an entity if need arises,
       
    51 * all entities have an `eid` attribute (an integer) that is its
       
    52   primary key (but it is possible to declare uniqueness on other
       
    53   attributes)
       
    54 
       
    55 Also |yams| supports the notions of:
       
    56 
       
    57 * entity inheritance (quite experimental yet, and completely
       
    58   undocumented),
       
    59 * relation type: that is, relationships can be established over a set
       
    60   of couple of entity types (henre the distinction made between
       
    61   `RelationType` and `RelationDefinition` below)
       
    62 
       
    63 Finally |yams| has a few concepts of its own:
       
    64 
       
    65 * relationships being oriented and binary, we call the left hand
       
    66   entity type the `subject` and the right hand entity type the
       
    67   `object`
       
    68 
       
    69 .. note::
       
    70 
       
    71    The |yams| schema is available at run time through the .schema
       
    72    attribute of the `vregistry`.  It's an instance of
       
    73    :class:`cubicweb.schema.Schema`, which extends
       
    74    :class:`yams.schema.Schema`.
       
    75 
       
    76 .. _EntityType:
       
    77 
       
    78 Entity type
       
    79 ~~~~~~~~~~~
       
    80 
       
    81 An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has
       
    82 a set of attributes and relations, and some permissions which define who can add, read,
       
    83 update or delete entities of this type.
       
    84 
       
    85 The following built-in types are available: ``String``,
       
    86 ``Int``, ``BigInt``, ``Float``, ``Decimal``, ``Boolean``,
       
    87 ``Date``, ``Datetime``, ``Time``, ``Interval``, ``Byte`` and
       
    88 ``Password``. They can only be used as attributes of an other entity
       
    89 type.
       
    90 
       
    91 There is also a `RichString` kindof type:
       
    92 
       
    93  .. autoclass:: yams.buildobjs.RichString
       
    94 
       
    95 The ``__unique_together__`` class attribute is a list of tuples of names of
       
    96 attributes or inlined relations.  For each tuple, CubicWeb ensures the unicity
       
    97 of the combination.  For example:
       
    98 
       
    99 .. sourcecode:: python
       
   100 
       
   101   class State(EntityType):
       
   102       __unique_together__ = [('name', 'state_of')]
       
   103 
       
   104       name = String(required=True)
       
   105       state_of = SubjectRelation('Workflow', cardinality='1*',
       
   106                                  composite='object', inlined=True)
       
   107 
       
   108 
       
   109 You can find more base entity types in
       
   110 :ref:`pre_defined_entity_types`.
       
   111 
       
   112 .. XXX yams inheritance
       
   113 
       
   114 .. _RelationType:
       
   115 
       
   116 Relation type
       
   117 ~~~~~~~~~~~~~
       
   118 
       
   119 A relation type is an instance of
       
   120 :class:`yams.schema.RelationSchema`. A relation type is simply a
       
   121 semantic definition of a kind of relationship that may occur in an
       
   122 application.
       
   123 
       
   124 It may be referenced by zero, one or more relation definitions.
       
   125 
       
   126 It is important to choose a good name, at least to avoid conflicts
       
   127 with some semantically different relation defined in other cubes
       
   128 (since there's only a shared name space for these names).
       
   129 
       
   130 A relation type holds the following properties (which are hence shared
       
   131 between all relation definitions of that type):
       
   132 
       
   133 * `inlined`: boolean handling the physical optimization for archiving
       
   134   the relation in the subject entity table, instead of creating a specific
       
   135   table for the relation. This applies to relations where cardinality
       
   136   of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation
       
   137   definitions.
       
   138 
       
   139 * `symmetric`: boolean indicating that the relation is symmetrical, which
       
   140   means that `X relation Y` implies `Y relation X`.
       
   141 
       
   142 .. _RelationDefinition:
       
   143 
       
   144 Relation definition
       
   145 ~~~~~~~~~~~~~~~~~~~
       
   146 
       
   147 A relation definition is an instance of
       
   148 :class:`yams.schema.RelationDefinition`. It is a complete triplet
       
   149 "<subject entity type> <relation type> <object entity type>".
       
   150 
       
   151 When creating a new instance of that class, the corresponding
       
   152 :class:`RelationType` instance is created on the fly if necessary.
       
   153 
       
   154 Properties
       
   155 ``````````
       
   156 
       
   157 The available properties for relation definitions are enumerated
       
   158 here. There are several kind of properties, as some relation
       
   159 definitions are actually attribute definitions, and other are not.
       
   160 
       
   161 Some properties may be completely optional, other may have a default
       
   162 value.
       
   163 
       
   164 Common properties for attributes and relations:
       
   165 
       
   166 * `description`: a unicode string describing an attribute or a
       
   167   relation. By default this string will be used in the editing form of
       
   168   the entity, which means that it is supposed to help the end-user and
       
   169   should be flagged by the function `_` to be properly
       
   170   internationalized.
       
   171 
       
   172 * `constraints`: a list of conditions/constraints that the relation has to
       
   173   satisfy (c.f. `Constraints`_)
       
   174 
       
   175 * `cardinality`: a two character string specifying the cardinality of
       
   176   the relation. The first character defines the cardinality of the
       
   177   relation on the subject, and the second on the object. When a
       
   178   relation can have multiple subjects or objects, the cardinality
       
   179   applies to all, not on a one-to-one basis (so it must be
       
   180   consistent...). Default value is '**'. The possible values are
       
   181   inspired from regular expression syntax:
       
   182 
       
   183     * `1`: 1..1
       
   184     * `?`: 0..1
       
   185     * `+`: 1..n
       
   186     * `*`: 0..n
       
   187 
       
   188 Attributes properties:
       
   189 
       
   190 * `unique`: boolean indicating if the value of the attribute has to be
       
   191   unique or not within all entities of the same type (false by
       
   192   default)
       
   193 
       
   194 * `indexed`: boolean indicating if an index needs to be created for
       
   195   this attribute in the database (false by default). This is useful
       
   196   only if you know that you will have to run numerous searches on the
       
   197   value of this attribute.
       
   198 
       
   199 * `default`: default value of the attribute. In case of date types, the values
       
   200   which could be used correspond to the RQL keywords `TODAY` and `NOW`.
       
   201 
       
   202 * `metadata`: Is also accepted as an argument of the attribute contructor. It is
       
   203   not really an attribute property. see `Metadata`_ for details.
       
   204 
       
   205 Properties for `String` attributes:
       
   206 
       
   207 * `fulltextindexed`: boolean indicating if the attribute is part of
       
   208   the full text index (false by default) (*applicable on the type
       
   209   `Byte` as well*)
       
   210 
       
   211 * `internationalizable`: boolean indicating if the value of the
       
   212   attribute is internationalizable (false by default)
       
   213 
       
   214 Relation properties:
       
   215 
       
   216 * `composite`: string indicating that the subject (composite ==
       
   217   'subject') is composed of the objects of the relations. For the
       
   218   opposite case (when the object is composed of the subjects of the
       
   219   relation), we just set 'object' as value. The composition implies
       
   220   that when the relation is deleted (so when the composite is deleted,
       
   221   at least), the composed are also deleted.
       
   222 
       
   223 * `fulltext_container`: string indicating if the value if the full
       
   224   text indexation of the entity on one end of the relation should be
       
   225   used to find the entity on the other end. The possible values are
       
   226   'subject' or 'object'. For instance the use_email relation has that
       
   227   property set to 'subject', since when performing a full text search
       
   228   people want to find the entity using an email address, and not the
       
   229   entity representing the email address.
       
   230 
       
   231 Constraints
       
   232 ```````````
       
   233 
       
   234 By default, the available constraint types are:
       
   235 
       
   236 General Constraints
       
   237 ......................
       
   238 
       
   239 * `SizeConstraint`: allows to specify a minimum and/or maximum size on
       
   240   string (generic case of `maxsize`)
       
   241 
       
   242 * `BoundaryConstraint`: allows to specify a minimum and/or maximum value
       
   243   on numeric types and date
       
   244 
       
   245 .. sourcecode:: python
       
   246 
       
   247    from yams.constraints import BoundaryConstraint, TODAY, NOW, Attribute
       
   248 
       
   249    class DatedEntity(EntityType):
       
   250       start = Date(constraints=[BoundaryConstraint('>=', TODAY())])
       
   251       end = Date(constraints=[BoundaryConstraint('>=', Attribute('start'))])
       
   252 
       
   253    class Before(EntityType);
       
   254       last_time = DateTime(constraints=[BoundaryConstraint('<=', NOW())])
       
   255 
       
   256 * `IntervalBoundConstraint`: allows to specify an interval with
       
   257   included values
       
   258 
       
   259 .. sourcecode:: python
       
   260 
       
   261      class Node(EntityType):
       
   262          latitude = Float(constraints=[IntervalBoundConstraint(-90, +90)])
       
   263 
       
   264 * `UniqueConstraint`: identical to "unique=True"
       
   265 
       
   266 * `StaticVocabularyConstraint`: identical to "vocabulary=(...)"
       
   267 
       
   268 Constraints can be dependent on a fixed value (90, Date(2015,3,23)) or a variable.
       
   269 In this second case, yams can handle :
       
   270 
       
   271 * `Attribute`: compare to the value of another attribute.
       
   272 * `TODAY`: compare to the current Date.
       
   273 * `NOW`: compare to the current Datetime.
       
   274 
       
   275 RQL Based Constraints
       
   276 ......................
       
   277 
       
   278 RQL based constraints may take three arguments. The first one is the ``WHERE``
       
   279 clause of a RQL query used by the constraint. The second argument ``mainvars``
       
   280 is the ``Any`` clause of the query. By default this include `S` reserved for the
       
   281 subject of the relation and `O` for the object. Additional variables could be
       
   282 specified using ``mainvars``. The argument expects a single string with all
       
   283 variable's name separated by spaces. The last one, ``msg``, is the error message
       
   284 displayed when the constraint fails. As RQLVocabularyConstraint never fails the
       
   285 third argument is not available.
       
   286 
       
   287 * `RQLConstraint`: allows to specify a RQL query that has to be satisfied
       
   288   by the subject and/or the object of relation. In this query the variables
       
   289   `S` and `O` are reserved for the relation subject and object entities.
       
   290 
       
   291 * `RQLVocabularyConstraint`: similar to the previous type of constraint except
       
   292   that it does not express a "strong" constraint, which means it is only used to
       
   293   restrict the values listed in the drop-down menu of editing form, but it does
       
   294   not prevent another entity to be selected.
       
   295 
       
   296 * `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an
       
   297   attribute is unique in a specific context. The Query must **never** return more
       
   298   than a single result to be satisfied. In this query the variables `S` is
       
   299   reserved for the relation subject entity. The other variables should be
       
   300   specified with the second constructor argument (mainvars). This constraint type
       
   301   should be used when __unique_together__ doesn't fit.
       
   302 
       
   303 .. XXX note about how to add new constraint
       
   304 
       
   305 .. _securitymodel:
       
   306 
       
   307 The security model
       
   308 ~~~~~~~~~~~~~~~~~~
       
   309 
       
   310 The security model of `CubicWeb` is based on `Access Control List`.
       
   311 The main principles are:
       
   312 
       
   313 * users and groups of users
       
   314 * a user belongs to at least one group of user
       
   315 * permissions (`read`, `update`, `create`, `delete`)
       
   316 * permissions are assigned to groups (and not to users)
       
   317 
       
   318 For *CubicWeb* in particular:
       
   319 
       
   320 * we associate rights at the entities/relations schema level
       
   321 
       
   322 * the default groups are: `managers`, `users` and `guests`
       
   323 
       
   324 * users belong to the `users` group
       
   325 
       
   326 * there is a virtual group called `owners` to which we can associate only
       
   327   `delete` and `update` permissions
       
   328 
       
   329   * we can not add users to the `owners` group, they are implicitly added to it
       
   330     according to the context of the objects they own
       
   331 
       
   332   * the permissions of this group are only checked on `update`/`delete` actions
       
   333     if all the other groups the user belongs to do not provide those permissions
       
   334 
       
   335 Setting permissions is done with the class attribute `__permissions__`
       
   336 of entity types and relation definitions. The value of this attribute
       
   337 is a dictionary where the keys are the access types (action), and the
       
   338 values are the authorized groups or rql expressions.
       
   339 
       
   340 For an entity type, the possible actions are `read`, `add`, `update` and
       
   341 `delete`.
       
   342 
       
   343 For a relation, the possible actions are `read`, `add`, and `delete`.
       
   344 
       
   345 For an attribute, the possible actions are `read`, `add` and `update`,
       
   346 and they are a refinement of an entity type permission.
       
   347 
       
   348 .. note::
       
   349 
       
   350    By default, the permissions of an entity type attributes are
       
   351    equivalent to the permissions of the entity type itself.
       
   352 
       
   353    It is possible to provide custom attribute permissions which are
       
   354    stronger than, or are more lenient than the entity type
       
   355    permissions.
       
   356 
       
   357    In a situation where all attributes were given custom permissions,
       
   358    the entity type permissions would not be checked, except for the
       
   359    `delete` action.
       
   360 
       
   361 For each access type, a tuple indicates the name of the authorized groups and/or
       
   362 one or multiple RQL expressions to satisfy to grant access. The access is
       
   363 provided if the user is in one of the listed groups or if one of the RQL condition
       
   364 is satisfied.
       
   365 
       
   366 Default permissions
       
   367 ```````````````````
       
   368 
       
   369 The default permissions for ``EntityType`` are:
       
   370 
       
   371 .. sourcecode:: python
       
   372 
       
   373    __permissions__ = {
       
   374         'read': ('managers', 'users', 'guests',),
       
   375         'update': ('managers', 'owners',),
       
   376         'delete': ('managers', 'owners'),
       
   377         'add': ('managers', 'users',)
       
   378         }
       
   379 
       
   380 The default permissions for relations are:
       
   381 
       
   382 .. sourcecode:: python
       
   383 
       
   384    __permissions__ = {'read': ('managers', 'users', 'guests',),
       
   385                     'delete': ('managers', 'users'),
       
   386                     'add': ('managers', 'users',)}
       
   387 
       
   388 The default permissions for attributes are:
       
   389 
       
   390 .. sourcecode:: python
       
   391 
       
   392    __permissions__ = {'read': ('managers', 'users', 'guests',),
       
   393                       'add': ('managers', ERQLExpression('U has_add_permission X'),
       
   394                       'update': ('managers', ERQLExpression('U has_update_permission X')),}
       
   395 
       
   396 .. note::
       
   397 
       
   398    The default permissions for attributes are not syntactically
       
   399    equivalent to the default permissions of the entity types, but the
       
   400    rql expressions work by delegating to the entity type permissions.
       
   401 
       
   402 
       
   403 The standard user groups
       
   404 ````````````````````````
       
   405 
       
   406 * `guests`
       
   407 
       
   408 * `users`
       
   409 
       
   410 * `managers`
       
   411 
       
   412 * `owners`: virtual group corresponding to the entity's owner.
       
   413   This can only be used for the actions `update` and `delete` of an entity
       
   414   type.
       
   415 
       
   416 It is also possible to use specific groups if they are defined in the precreate
       
   417 script of the cube (``migration/precreate.py``). Defining groups in postcreate
       
   418 script or later makes them unavailable for security purposes (in this case, an
       
   419 `sync_schema_props_perms` command has to be issued in a CubicWeb shell).
       
   420 
       
   421 
       
   422 Use of RQL expression for write permissions
       
   423 ```````````````````````````````````````````
       
   424 
       
   425 It is possible to define RQL expression to provide update permission (`add`,
       
   426 `delete` and `update`) on entity type / relation definitions. An rql expression
       
   427 is a piece of query (corresponds to the WHERE statement of an RQL query), and the
       
   428 expression will be considered as satisfied if it returns some results. They can
       
   429 not be used in `read` permission.
       
   430 
       
   431 To use RQL expression in entity type permission:
       
   432 
       
   433 * you have to use the class :class:`~cubicweb.schema.ERQLExpression`
       
   434 
       
   435 * in this expression, the variables `X` and `U` are pre-defined references
       
   436   respectively on the current entity (on which the action is verified) and on the
       
   437   user who send the request
       
   438 
       
   439 For RQL expressions on a relation type, the principles are the same except for
       
   440 the following:
       
   441 
       
   442 * you have to use the class :class:`~cubicweb.schema.RRQLExpression` instead of
       
   443   :class:`~cubicweb.schema.ERQLExpression`
       
   444 
       
   445 * in the expression, the variables `S`, `O` and `U` are pre-defined references to
       
   446   respectively the subject and the object of the current relation (on which the
       
   447   action is being verified) and the user who executed the query
       
   448 
       
   449 To define security for attributes of an entity (non-final relation), you have to
       
   450 use the class :class:`~cubicweb.schema.ERQLExpression` in which `X` represents
       
   451 the entity the attribute belongs to.
       
   452 
       
   453 It is possible to use in those expression a special relation
       
   454 `has_<ACTION>_permission` where the subject is the user (eg 'U') and the object
       
   455 is any variable representing an entity (usually 'X' in
       
   456 :class:`~cubicweb.schema.ERQLExpression`, 'S' or 'O' in
       
   457 :class:`~cubicweb.schema.RRQLExpression`), meaning that the user needs to have
       
   458 permission to execute the action <ACTION> on the entities represented by this
       
   459 variable. It's recommanded to use this feature whenever possible since it
       
   460 simplify greatly complex security definition and upgrade.
       
   461 
       
   462 
       
   463 .. sourcecode:: python
       
   464 
       
   465   class my_relation(RelationDefinition):
       
   466     __permissions__ = {'read': ('managers', 'users'),
       
   467                        'add': ('managers', RRQLExpression('U has_update_permission S')),
       
   468                        'delete': ('managers', RRQLExpression('U has_update_permission S'))
       
   469 		       }
       
   470 
       
   471 In the above example, user will be allowed to add/delete `my_relation` if he has
       
   472 the `update` permission on the subject of the relation.
       
   473 
       
   474 .. note::
       
   475 
       
   476   Potentially, the `use of an RQL expression to add an entity or a relation` can
       
   477   cause problems for the user interface, because if the expression uses the
       
   478   entity or the relation to create, we are not able to verify the permissions
       
   479   before we actually added the entity (please note that this is not a problem for
       
   480   the RQL server at all, because the permissions checks are done after the
       
   481   creation). In such case, the permission check methods
       
   482   (CubicWebEntitySchema.check_perm and has_perm) can indicate that the user is
       
   483   not allowed to create this entity while it would obtain the permission.  To
       
   484   compensate this problem, it is usually necessary in such case to use an action
       
   485   that reflects the schema permissions but which check properly the permissions
       
   486   so that it would show up only if possible.
       
   487 
       
   488 
       
   489 Use of RQL expression for reading rights
       
   490 ````````````````````````````````````````
       
   491 
       
   492 The principles are the same but with the following restrictions:
       
   493 
       
   494 * you can not use rql expression for the `read` permission of relations and
       
   495   attributes,
       
   496 
       
   497 * you can not use special `has_<ACTION>_permission` relation in the rql
       
   498   expression.
       
   499 
       
   500 
       
   501 Important notes about write permissions checking
       
   502 ````````````````````````````````````````````````
       
   503 
       
   504 Write permissions (e.g. 'add', 'update', 'delete') are checked in core hooks.
       
   505 
       
   506 When a permission is checked slightly vary according to if it's an entity or
       
   507 relation, and if the relation is an attribute relation or not). It's important to
       
   508 understand that since according to when a permission is checked, values returned
       
   509 by rql expressions may changes, hence the permission being granted or not.
       
   510 
       
   511 Here are the current rules:
       
   512 
       
   513 1. permission to add/update entity and its attributes are checked on
       
   514    commit
       
   515 
       
   516 2. permission to delete an entity is checked in 'before_delete_entity' hook
       
   517 
       
   518 3. permission to add a relation is checked either:
       
   519 
       
   520    - in 'before_add_relation' hook if the relation type is in the
       
   521      `BEFORE_ADD_RELATIONS` set
       
   522 
       
   523    - else at commit time if the relation type is in the `ON_COMMIT_ADD_RELATIONS`
       
   524      set
       
   525 
       
   526    - else in 'after_add_relation' hook (the default)
       
   527 
       
   528 4. permission to delete a relation is checked in 'before_delete_relation' hook
       
   529 
       
   530 Last but not least, remember queries issued from hooks and operation are by
       
   531 default 'unsafe', eg there are no read or write security checks.
       
   532 
       
   533 See :mod:`cubicweb.hooks.security` for more details.
       
   534 
       
   535 
       
   536 .. _yams_example:
       
   537 
       
   538 
       
   539 Derived attributes and relations
       
   540 --------------------------------
       
   541 
       
   542 .. note:: **TODO** Check organisation of the whole chapter of the documentation
       
   543 
       
   544 Cubicweb offers the possibility to *query* data using so called
       
   545 *computed* relations and attributes. Those are *seen* by RQL requests
       
   546 as normal attributes and relations but are actually derived from other
       
   547 attributes and relations. In a first section we'll informally review
       
   548 two typical use cases. Then we see how to use computed attributes and
       
   549 relations in your schema. Last we will consider various significant
       
   550 aspects of their implementation and the impact on their usage.
       
   551 
       
   552 Motivating use cases
       
   553 ~~~~~~~~~~~~~~~~~~~~
       
   554 
       
   555 Computed (or reified) relations
       
   556 ```````````````````````````````
       
   557 
       
   558 It often arises that one must represent a ternary relation, or a
       
   559 family of relations. For example, in the context of an exhibition
       
   560 catalog you might want to link all *contributors* to the *work* they
       
   561 contributed to, but this contribution can be as *illustrator*,
       
   562 *author*, *performer*, ...
       
   563 
       
   564 The classical way to describe this kind of information within an
       
   565 entity-relationship schema is to *reify* the relation, that is turn
       
   566 the relation into a entity. In our example the schema will have a
       
   567 *Contribution* entity type used to represent the family of the
       
   568 contribution relations.
       
   569 
       
   570 
       
   571 .. sourcecode:: python
       
   572 
       
   573     class ArtWork(EntityType):
       
   574         name = String()
       
   575         ...
       
   576 
       
   577     class Person(EntityType):
       
   578         name = String()
       
   579         ...
       
   580 
       
   581     class Contribution(EntityType):
       
   582         contributor = SubjectRelation('Person', cardinality='1*', inlined=True)
       
   583         manifestation = SubjectRelation('ArtWork')
       
   584         role = SubjectRelation('Role')
       
   585 
       
   586     class Role(EntityType):
       
   587         name = String()
       
   588 
       
   589 But then, in order to query the illustrator(s) ``I`` of a work ``W``,
       
   590 one has to write::
       
   591 
       
   592     Any I, W WHERE C is Contribution, C contributor I, C manifestation W,
       
   593                    C role R, R name 'illustrator'
       
   594 
       
   595 whereas we would like to be able to simply write::
       
   596 
       
   597     Any I, W WHERE I illustrator_of W
       
   598 
       
   599 This is precisely what the computed relations allow.
       
   600 
       
   601 
       
   602 Computed (or synthesized) attribute
       
   603 ```````````````````````````````````
       
   604 
       
   605 Assuming a trivial schema for describing employees in companies, one
       
   606 can be interested in the total of salaries payed by a company for
       
   607 all its employees. One has to write::
       
   608 
       
   609     Any C, SUM(SA) GROUPBY S WHERE E works_for C, E salary SA
       
   610 
       
   611 whereas it would be most convenient to simply write::
       
   612 
       
   613     Any C, TS WHERE C total_salary TS
       
   614 
       
   615 And this is again what computed attributes provide.
       
   616 
       
   617 
       
   618 Using computed attributes and relations
       
   619 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   620 
       
   621 Computed (or reified) relations
       
   622 ```````````````````````````````
       
   623 
       
   624 In the above case we would define the *computed relation*
       
   625 ``illustrator_of`` in the schema by:
       
   626 
       
   627 .. sourcecode:: python
       
   628 
       
   629     class illustrator_of(ComputedRelation):
       
   630         rule  = ('C is Contribution, C contributor S, C manifestation O,'
       
   631                  'C role R, R name "illustrator"')
       
   632 
       
   633 You will note that:
       
   634 
       
   635 * the ``S`` and ``O`` RQL variables implicitly identify the subject and
       
   636   object of the defined computed relation, akin to what happens in
       
   637   RRQLExpression
       
   638 * the possible subject and object entity types are inferred from the rule;
       
   639 * computed relation definitions always have empty *add* and *delete* permissions
       
   640 * *read* permissions can be defined, permissions from the relations used in the
       
   641   rewrite rule **are not considered** ;
       
   642 * nothing else may be defined on the `ComputedRelation` subclass beside
       
   643   description, permissions and rule (e.g. no cardinality, composite, etc.,).
       
   644   `BadSchemaDefinition` is raised on attempt to specify other attributes;
       
   645 * computed relations can not be used in 'SET' and 'DELETE' rql queries
       
   646   (`BadQuery` exception raised).
       
   647 
       
   648 
       
   649 NB: The fact that the *add* and *delete* permissions are *empty* even
       
   650 for managers is expected to make the automatic UI not attempt to edit
       
   651 them.
       
   652 
       
   653 Computed (or synthesized) attributes
       
   654 ````````````````````````````````````
       
   655 
       
   656 In the above case we would define the *computed attribute*
       
   657 ``total_salary`` on the ``Company`` entity type in the schema by:
       
   658 
       
   659 .. sourcecode:: python
       
   660 
       
   661     class Company(EntityType):
       
   662         name = String()
       
   663         total_salary = Int(formula='Any SUM(SA) GROUPBY E WHERE P works_for X, E salary SA')
       
   664 
       
   665 * the ``X`` RQL variable implicitly identifies the entity holding the
       
   666   computed attribute, akin to what happens in ERQLExpression;
       
   667 * the type inferred from the formula is checked against the declared type, and
       
   668   `BadSchemaDefinition` is raised if they don't match;
       
   669 * the computed attributes always have empty *update* permissions
       
   670 * `BadSchemaDefinition` is raised on attempt to set 'update' permissions;
       
   671 * 'read' permissions can be defined, permissions regarding the formula
       
   672   **are not considered**;
       
   673 * other attribute's property (inlined, ...) can be defined as for normal attributes;
       
   674 * Similarly to computed relation, computed attribute can't be used in 'SET' and
       
   675   'DELETE' rql queries (`BadQuery` exception raised).
       
   676 
       
   677 
       
   678 API and implementation
       
   679 ~~~~~~~~~~~~~~~~~~~~~~
       
   680 
       
   681 Representation in the data backend
       
   682 ``````````````````````````````````
       
   683 
       
   684 Computed relations have no direct representation at the SQL table
       
   685 level.  Instead, each time a query is issued the query is rewritten to
       
   686 replace the computed relation by its equivalent definition and the
       
   687 resulting rewritten query is performed in the usual way.
       
   688 
       
   689 On the contrary, computed attributes are represented as a column in the
       
   690 table for their host entity type, just like normal attributes. Their
       
   691 value is kept up-to-date with respect to their defintion by a system
       
   692 of hooks (also called triggers in most RDBMS) which recomputes them
       
   693 when the relations and attributes they depend on are modified.
       
   694 
       
   695 Yams API
       
   696 ````````
       
   697 
       
   698 When accessing the schema through the *yams API* (not when defining a
       
   699 schema in a ``schema.py`` file) the computed attributes and relations
       
   700 are represented as follows:
       
   701 
       
   702 relations
       
   703     The ``yams.RelationSchema`` class has a new ``rule`` attribute
       
   704     holding the rule as a string. If this attribute is set all others
       
   705     must not be set.
       
   706 attributes
       
   707     A new property ``formula`` is added on class
       
   708     ``yams.RelationDefinitionSchema`` alomng with a new keyword
       
   709     argument ``formula`` on the initializer.
       
   710 
       
   711 Migration
       
   712 `````````
       
   713 
       
   714 The migrations are to be handled as summarized in the array below.
       
   715 
       
   716 +------------+---------------------------------------------------+---------------------------------------+
       
   717 |            | Computed rtype                                    | Computed attribute                    |
       
   718 +============+===================================================+=======================================+
       
   719 | add        | * add_relation_type                               | * add_attribute                       |
       
   720 |            | * add_relation_definition should trigger an error | * add_relation_definition             |
       
   721 +------------+---------------------------------------------------+---------------------------------------+
       
   722 | modify     | * sync_schema_prop_perms:                         | * sync_schema_prop_perms:             |
       
   723 |            |   checks the rule is                              |                                       |
       
   724 | (rule or   |   synchronized with the database                  |   - empty the cache,                  |
       
   725 | formula)   |                                                   |   - check formula,                    |
       
   726 |            |                                                   |   - make sure all the values get      |
       
   727 |            |                                                   |     updated                           |
       
   728 +------------+---------------------------------------------------+---------------------------------------+
       
   729 | del        | * drop_relation_type                              | * drop_attribute                      |
       
   730 |            | * drop_relation_definition should trigger an error| * drop_relation_definition            |
       
   731 +------------+---------------------------------------------------+---------------------------------------+
       
   732 
       
   733 
       
   734 Defining your schema using yams
       
   735 -------------------------------
       
   736 
       
   737 Entity type definition
       
   738 ~~~~~~~~~~~~~~~~~~~~~~
       
   739 
       
   740 An entity type is defined by a Python class which inherits from
       
   741 :class:`yams.buildobjs.EntityType`.  The class definition contains the
       
   742 description of attributes and relations for the defined entity type.
       
   743 The class name corresponds to the entity type name. It is expected to
       
   744 be defined in the module ``mycube.schema``.
       
   745 
       
   746 :Note on schema definition:
       
   747 
       
   748  The code in ``mycube.schema`` is not meant to be executed. The class
       
   749  EntityType mentioned above is different from the EntitySchema class
       
   750  described in the previous chapter. EntityType is a helper class to
       
   751  make Entity definition easier. Yams will process EntityType classes
       
   752  and create EntitySchema instances from these class definitions. Similar
       
   753  manipulation happen for relations.
       
   754 
       
   755 When defining a schema using python files, you may use the following shortcuts:
       
   756 
       
   757 - `required`: boolean indicating if the attribute is required, ed subject cardinality is '1'
       
   758 
       
   759 - `vocabulary`: specify static possible values of an attribute
       
   760 
       
   761 - `maxsize`: integer providing the maximum size of a string (no limit by default)
       
   762 
       
   763 For example:
       
   764 
       
   765 .. sourcecode:: python
       
   766 
       
   767   class Person(EntityType):
       
   768     """A person with the properties and the relations necessary for my
       
   769     application"""
       
   770 
       
   771     last_name = String(required=True, fulltextindexed=True)
       
   772     first_name = String(required=True, fulltextindexed=True)
       
   773     title = String(vocabulary=('Mr', 'Mrs', 'Miss'))
       
   774     date_of_birth = Date()
       
   775     works_for = SubjectRelation('Company', cardinality='?*')
       
   776 
       
   777 
       
   778 The entity described above defines three attributes of type String,
       
   779 last_name, first_name and title, an attribute of type Date for the date of
       
   780 birth and a relation that connects a `Person` to another entity of type
       
   781 `Company` through the semantic `works_for`.
       
   782 
       
   783 
       
   784 
       
   785 :Naming convention:
       
   786 
       
   787  Entity class names must start with an uppercase letter. The common
       
   788  usage is to use ``CamelCase`` names.
       
   789 
       
   790  Attribute and relation names must start with a lowercase letter. The
       
   791  common usage is to use ``underscore_separated_words``. Attribute and
       
   792  relation names starting with a single underscore are permitted, to
       
   793  denote a somewhat "protected" or "private" attribute.
       
   794 
       
   795  In any case, identifiers starting with "CW" or "cw" are reserved for
       
   796  internal use by the framework.
       
   797 
       
   798  .. _Metadata:
       
   799 
       
   800  Some attribute using the name of another attribute as prefix are considered
       
   801  metadata.  For example, if an EntityType have both a ``data`` and
       
   802  ``data_format`` attribute, ``data_format`` is view as the ``format`` metadata
       
   803  of ``data``. Later the :meth:`cw_attr_metadata` method will allow you to fetch
       
   804  metadata related to an attribute. There are only three valid metadata names:
       
   805  ``format``, ``encoding`` and ``name``.
       
   806 
       
   807 
       
   808 The name of the Python attribute corresponds to the name of the attribute
       
   809 or the relation in *CubicWeb* application.
       
   810 
       
   811 An attribute is defined in the schema as follows::
       
   812 
       
   813     attr_name = AttrType(*properties, metadata={})
       
   814 
       
   815 where
       
   816 
       
   817 * `AttrType`: is one of the type listed in EntityType_,
       
   818 
       
   819 * `properties`: is a list of the attribute needs to satisfy (see `Properties`_
       
   820   for more details),
       
   821 
       
   822 * `metadata`: is a dictionary of meta attributes related to ``attr_name``.
       
   823   Dictionary keys are the name of the meta attribute. Dictionary values
       
   824   attributes objects (like the content of ``AttrType``). For each entry of the
       
   825   metadata dictionary a ``<attr_name>_<key> = <value>`` attribute is
       
   826   automaticaly added to the EntityType.  see `Metadata`_ section for details
       
   827   about valid key.
       
   828 
       
   829 
       
   830  ---
       
   831 
       
   832 While building your schema
       
   833 
       
   834 * it is possible to use the attribute `meta` to flag an entity type as a `meta`
       
   835   (e.g. used to describe/categorize other entities)
       
   836 
       
   837 .. XXX the paragraph below needs clarification and / or moving out in
       
   838 .. another place
       
   839 
       
   840 *Note*: if you end up with an `if` in the definition of your entity, this probably
       
   841 means that you need two separate entities that implement the `ITree` interface and
       
   842 get the result from `.children()` which ever entity is concerned.
       
   843 
       
   844 .. Inheritance
       
   845 .. ```````````
       
   846 .. XXX feed me
       
   847 
       
   848 
       
   849 Definition of relations
       
   850 ~~~~~~~~~~~~~~~~~~~~~~~
       
   851 
       
   852 .. XXX add note about defining relation type / definition
       
   853 
       
   854 A relation is defined by a Python class heriting `RelationType`. The name
       
   855 of the class corresponds to the name of the type. The class then contains
       
   856 a description of the properties of this type of relation, and could as well
       
   857 contain a string for the subject and a string for the object. This allows to create
       
   858 new definition of associated relations, (so that the class can have the
       
   859 definition properties from the relation) for example ::
       
   860 
       
   861   class locked_by(RelationType):
       
   862     """relation on all entities indicating that they are locked"""
       
   863     inlined = True
       
   864     cardinality = '?*'
       
   865     subject = '*'
       
   866     object = 'CWUser'
       
   867 
       
   868 If provided, the `subject` and `object` attributes denote the subject
       
   869 and object of the various relation definitions related to the relation
       
   870 type. Allowed values for these attributes are:
       
   871 
       
   872 * a string corresponding to an entity type
       
   873 * a tuple of string corresponding to multiple entity types
       
   874 * the '*' special string, meaning all types of entities
       
   875 
       
   876 When a relation is not inlined and not symmetrical, and it does not require
       
   877 specific permissions, it can be defined using a `SubjectRelation`
       
   878 attribute in the EntityType class. The first argument of `SubjectRelation` gives
       
   879 the entity type for the object of the relation.
       
   880 
       
   881 :Naming convention:
       
   882 
       
   883  Although this way of defining relations uses a Python class, the
       
   884  naming convention defined earlier prevails over the PEP8 conventions
       
   885  used in the framework: relation type class names use
       
   886  ``underscore_separated_words``.
       
   887 
       
   888 :Historical note:
       
   889 
       
   890    It has been historically possible to use `ObjectRelation` which
       
   891    defines a relation in the opposite direction. This feature is
       
   892    deprecated and therefore should not be used in newly written code.
       
   893 
       
   894 :Future deprecation note:
       
   895 
       
   896   In an even more remote future, it is quite possible that the
       
   897   SubjectRelation shortcut will become deprecated, in favor of the
       
   898   RelationType declaration which offers some advantages in the context
       
   899   of reusable cubes.
       
   900 
       
   901 
       
   902 
       
   903 
       
   904 Handling schema changes
       
   905 ~~~~~~~~~~~~~~~~~~~~~~~
       
   906 
       
   907 Also, it should be clear that to properly handle data migration, an
       
   908 instance's schema is stored in the database, so the python schema file
       
   909 used to defined it is only read when the instance is created or
       
   910 upgraded.
       
   911 
       
   912 .. XXX complete me