1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 Yams *schema* |
|
4 ------------- |
|
5 |
|
6 The **schema** is the core piece of a *CubicWeb* instance as it |
|
7 defines and handles the data model. It is based on entity types that |
|
8 are either already defined in `Yams`_ and the *CubicWeb* standard |
|
9 library; or more specific types defined in cubes. The schema for a |
|
10 cube is defined in a `schema` python module or package. |
|
11 |
|
12 .. _`Yams`: http://www.logilab.org/project/yams |
|
13 |
|
14 Overview |
|
15 ~~~~~~~~ |
|
16 |
|
17 The core idea of the yams schema is not far from the classical |
|
18 `Entity-relationship`_ model. But while an E/R model (or `logical |
|
19 model`) traditionally has to be manually translated to a lower-level |
|
20 data description language (such as the SQL `create table` |
|
21 sublanguage), also often described as the `physical model`, no such |
|
22 step is required with |yams| and |cubicweb|. |
|
23 |
|
24 .. _`Entity-relationship`: http://en.wikipedia.org/wiki/Entity-relationship_model |
|
25 |
|
26 This is because in addition to high-level, logical |yams| models, one |
|
27 uses the |rql| data manipulation language to query, insert, update and |
|
28 delete data. |rql| abstracts as much of the underlying SQL database as |
|
29 a |yams| schema abstracts from the physical layout. The vagaries of |
|
30 SQL are avoided. |
|
31 |
|
32 As a bonus point, such abstraction make it quite comfortable to build |
|
33 or use different backends to which |rql| queries apply. |
|
34 |
|
35 So, as in the E/R formalism, the building blocks are ``entities`` |
|
36 (:ref:`EntityType`), ``relationships`` (:ref:`RelationType`, |
|
37 :ref:`RelationDefinition`) and ``attributes`` (handled like relation |
|
38 with |yams|). |
|
39 |
|
40 Let us detail a little the divergences between E/R and |yams|: |
|
41 |
|
42 * all relationship are binary which means that to represent a |
|
43 non-binary relationship, one has to use an entity, |
|
44 * relationships do not support attributes (yet, see: |
|
45 http://www.cubicweb.org/ticket/341318), hence the need to reify it |
|
46 as an entity if need arises, |
|
47 * all entities have an `eid` attribute (an integer) that is its |
|
48 primary key (but it is possible to declare uniqueness on other |
|
49 attributes) |
|
50 |
|
51 Also |yams| supports the notions of: |
|
52 |
|
53 * entity inheritance, |
|
54 * relation type: that is, relationships can be established over a set |
|
55 of couple of entity types (henre the distinction made between |
|
56 `RelationType` and `RelationDefinition` below) |
|
57 |
|
58 Finally |yams| has a few concepts of its own: |
|
59 |
|
60 * relationships being oriented and binary, we call the left hand |
|
61 entity type the `subject` and the right hand entity type the |
|
62 `object` |
|
63 |
|
64 .. note:: |
|
65 |
|
66 The |yams| schema is available at run time through the .schema |
|
67 attribute of the `vregistry`. It's an instance of |
|
68 :class:`cubicweb.schema.Schema`, which extends |
|
69 :class:`yams.schema.Schema`. |
|
70 |
|
71 .. _EntityType: |
|
72 |
|
73 Entity type |
|
74 ~~~~~~~~~~~ |
|
75 |
|
76 An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has |
|
77 a set of attributes and relations, and some permissions which define who can add, read, |
|
78 update or delete entities of this type. |
|
79 |
|
80 The following built-in types are available: ``String``, ``Int``, |
|
81 ``Float``, ``Decimal``, ``Boolean``, ``Date``, ``Datetime``, ``Time``, |
|
82 ``Interval``, ``Byte`` and ``Password``. They can only be used as |
|
83 attributes of an other entity type. |
|
84 |
|
85 You can find more base entity types in |
|
86 :ref:`pre_defined_entity_types`. |
|
87 |
|
88 .. XXX yams inheritance |
|
89 |
|
90 .. _RelationType: |
|
91 |
|
92 Relation type |
|
93 ~~~~~~~~~~~~~ |
|
94 |
|
95 A relation type is an instance of |
|
96 :class:`yams.schema.RelationSchema`. A relation type is simply a |
|
97 semantic definition of a kind of relationship that may occur in an |
|
98 application. |
|
99 |
|
100 It may be referenced by zero, one or more relation definitions. |
|
101 |
|
102 It is important to choose a good name, at least to avoid conflicts |
|
103 with some semantically different relation defined in other cubes |
|
104 (since there's only a shared name space for these names). |
|
105 |
|
106 A relation type holds the following properties (which are hence shared |
|
107 between all relation definitions of that type): |
|
108 |
|
109 * `inlined`: boolean handling the physical optimization for archiving |
|
110 the relation in the subject entity table, instead of creating a specific |
|
111 table for the relation. This applies to relations where cardinality |
|
112 of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation |
|
113 definitions. |
|
114 |
|
115 * `symmetric`: boolean indicating that the relation is symmetrical, which |
|
116 means that `X relation Y` implies `Y relation X`. |
|
117 |
|
118 .. _RelationDefinition: |
|
119 |
|
120 Relation definition |
|
121 ~~~~~~~~~~~~~~~~~~~ |
|
122 |
|
123 A relation definition is an instance of |
|
124 :class:`yams.schema.RelationDefinition`. It is a complete triplet |
|
125 "<subject entity type> <relation type> <object entity type>". |
|
126 |
|
127 When creating a new instance of that class, the corresponding |
|
128 :class:`RelationType` instance is created on the fly if necessary. |
|
129 |
|
130 Properties |
|
131 `````````` |
|
132 |
|
133 The available properties for relation definitions are enumerated |
|
134 here. There are several kind of properties, as some relation |
|
135 definitions are actually attribute definitions, and other are not. |
|
136 |
|
137 Some properties may be completely optional, other may have a default |
|
138 value. |
|
139 |
|
140 Common properties for attributes and relations: |
|
141 |
|
142 * `description`: an unicode string describing an attribute or a |
|
143 relation. By default this string will be used in the editing form of |
|
144 the entity, which means that it is supposed to help the end-user and |
|
145 should be flagged by the function `_` to be properly |
|
146 internationalized. |
|
147 |
|
148 * `constraints`: a list of conditions/constraints that the relation has to |
|
149 satisfy (c.f. `Constraints`_) |
|
150 |
|
151 * `cardinality`: a two character string specifying the cardinality of |
|
152 the relation. The first character defines the cardinality of the |
|
153 relation on the subject, and the second on the object. When a |
|
154 relation can have multiple subjects or objects, the cardinality |
|
155 applies to all, not on a one-to-one basis (so it must be |
|
156 consistent...). Default value is '**'. The possible values are |
|
157 inspired from regular expression syntax: |
|
158 |
|
159 * `1`: 1..1 |
|
160 * `?`: 0..1 |
|
161 * `+`: 1..n |
|
162 * `*`: 0..n |
|
163 |
|
164 Attributes properties: |
|
165 |
|
166 * `unique`: boolean indicating if the value of the attribute has to be |
|
167 unique or not within all entities of the same type (false by |
|
168 default) |
|
169 |
|
170 * `indexed`: boolean indicating if an index needs to be created for |
|
171 this attribute in the database (false by default). This is useful |
|
172 only if you know that you will have to run numerous searches on the |
|
173 value of this attribute. |
|
174 |
|
175 * `default`: default value of the attribute. In case of date types, the values |
|
176 which could be used correspond to the RQL keywords `TODAY` and `NOW`. |
|
177 |
|
178 Properties for `String` attributes: |
|
179 |
|
180 * `fulltextindexed`: boolean indicating if the attribute is part of |
|
181 the full text index (false by default) (*applicable on the type |
|
182 `Byte` as well*) |
|
183 |
|
184 * `internationalizable`: boolean indicating if the value of the |
|
185 attribute is internationalizable (false by default) |
|
186 |
|
187 Relation properties: |
|
188 |
|
189 * `composite`: string indicating that the subject (composite == |
|
190 'subject') is composed of the objects of the relations. For the |
|
191 opposite case (when the object is composed of the subjects of the |
|
192 relation), we just set 'object' as value. The composition implies |
|
193 that when the relation is deleted (so when the composite is deleted, |
|
194 at least), the composed are also deleted. |
|
195 |
|
196 * `fulltext_container`: string indicating if the value if the full |
|
197 text indexation of the entity on one end of the relation should be |
|
198 used to find the entity on the other end. The possible values are |
|
199 'subject' or 'object'. For instance the use_email relation has that |
|
200 property set to 'subject', since when performing a full text search |
|
201 people want to find the entity using an email address, and not the |
|
202 entity representing the email address. |
|
203 |
|
204 Constraints |
|
205 ``````````` |
|
206 |
|
207 By default, the available constraint types are: |
|
208 |
|
209 General Constraints |
|
210 ...................... |
|
211 |
|
212 * `SizeConstraint`: allows to specify a minimum and/or maximum size on |
|
213 string (generic case of `maxsize`) |
|
214 |
|
215 * `BoundConstraint`: allows to specify a minimum and/or maximum value |
|
216 on numeric types and date |
|
217 |
|
218 .. sourcecode:: python |
|
219 |
|
220 from yams.constraints import BoundConstraint, TODAY |
|
221 BoundConstraint('<=', TODAY()) |
|
222 |
|
223 * `IntervalBoundConstraint`: allows to specify an interval with |
|
224 included values |
|
225 |
|
226 .. sourcecode:: python |
|
227 |
|
228 class Node(EntityType): |
|
229 latitude = Float(constraints=[IntervalBoundConstraint(-90, +90)]) |
|
230 |
|
231 * `UniqueConstraint`: identical to "unique=True" |
|
232 |
|
233 * `StaticVocabularyConstraint`: identical to "vocabulary=(...)" |
|
234 |
|
235 .. XXX Attribute, NOW |
|
236 |
|
237 RQL Based Constraints |
|
238 ...................... |
|
239 |
|
240 RQL based constraints may take three arguments. The first one is the ``WHERE`` |
|
241 clause of a RQL query used by the constraint. The second argument ``mainvars`` |
|
242 is the ``Any`` clause of the query. By default this include `S` reserved for the |
|
243 subject of the relation and `O` for the object. Additional variables could be |
|
244 specified using ``mainvars``. The argument expects a single string with all |
|
245 variable's name separated by spaces. The last one, ``msg``, is the error message |
|
246 displayed when the constraint fails. As RQLVocabularyConstraint never fails the |
|
247 third argument is not available. |
|
248 |
|
249 * `RQLConstraint`: allows to specify a RQL query that has to be satisfied |
|
250 by the subject and/or the object of relation. In this query the variables |
|
251 `S` and `O` are reserved for the relation subject and object entities. |
|
252 |
|
253 * `RQLVocabularyConstraint`: similar to the previous type of constraint except |
|
254 that it does not express a "strong" constraint, which means it is only used to |
|
255 restrict the values listed in the drop-down menu of editing form, but it does |
|
256 not prevent another entity to be selected. |
|
257 |
|
258 * `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an |
|
259 attribute is unique in a specific context. The Query must **never** return more |
|
260 than a single result to be satisfied. In this query the variables `S` is |
|
261 reserved for the relation subject entity. The other variables should be |
|
262 specified with the second constructor argument (mainvars). This constraints |
|
263 should be used when UniqueConstraint doesn't fit. Here is a simple example. |
|
264 |
|
265 .. sourcecode:: python |
|
266 |
|
267 # Check that in the same Workflow each state's name is unique. Using |
|
268 # UniqueConstraint (or unique=True) here would prevent states in different |
|
269 # workflows to have the same name. |
|
270 |
|
271 # With: State S, Workflow W, String N ; S state_of W, S name N |
|
272 |
|
273 RQLUniqueConstraint('S name N, S state_of WF, Y state_of WF, Y name N', |
|
274 mainvars='Y', |
|
275 msg=_('workflow already has a state of that name')) |
|
276 |
|
277 .. XXX note about how to add new constraint |
|
278 |
|
279 .. _securitymodel: |
|
280 |
|
281 The security model |
|
282 ~~~~~~~~~~~~~~~~~~ |
|
283 |
|
284 The security model of `CubicWeb` is based on `Access Control List`. |
|
285 The main principles are: |
|
286 |
|
287 * users and groups of users |
|
288 * a user belongs to at least one group of user |
|
289 * permissions (read, update, create, delete) |
|
290 * permissions are assigned to groups (and not to users) |
|
291 |
|
292 For *CubicWeb* in particular: |
|
293 |
|
294 * we associate rights at the entities/relations schema level |
|
295 * for each entity, we distinguish four kinds of permissions: `read`, |
|
296 `add`, `update` and `delete` |
|
297 * for each relation, we distinguish three kinds of permissions: `read`, |
|
298 `add` and `delete` (it is not possible to `modify` a relation) |
|
299 * the default groups are: `administrators`, `users` and `guests` |
|
300 * by default, users belong to the `users` group |
|
301 * there is a virtual group called `owners` to which we |
|
302 can associate only `delete` and `update` permissions |
|
303 |
|
304 * we can not add users to the `Owners` group, they are |
|
305 implicitly added to it according to the context of the objects |
|
306 they own |
|
307 * the permissions of this group are only checked on `update`/`delete` |
|
308 actions if all the other groups the user belongs to do not provide |
|
309 those permissions |
|
310 |
|
311 Setting permissions is done with the attribute `__permissions__` of entities and |
|
312 relation types. The value of this attribute is a dictionary where the keys are the access types |
|
313 (action), and the values are the authorized groups or expressions. |
|
314 |
|
315 For an entity type, the possible actions are `read`, `add`, `update` and |
|
316 `delete`. |
|
317 |
|
318 For a relation type, the possible actions are `read`, `add`, and `delete`. |
|
319 |
|
320 For each access type, a tuple indicates the name of the authorized groups and/or |
|
321 one or multiple RQL expressions to satisfy to grant access. The access is |
|
322 provided if the user is in one of the listed groups or if one of the RQL condition |
|
323 is satisfied. |
|
324 |
|
325 The standard user groups |
|
326 ```````````````````````` |
|
327 |
|
328 * `guests` |
|
329 |
|
330 * `users` |
|
331 |
|
332 * `managers` |
|
333 |
|
334 * `owners`: virtual group corresponding to the entity's owner. |
|
335 This can only be used for the actions `update` and `delete` of an entity |
|
336 type. |
|
337 |
|
338 It is also possible to use specific groups if they are defined in the |
|
339 precreate script of the cube (``migration/precreate.py``). Defining groups in |
|
340 postcreate script or later makes them unavailable for security |
|
341 purposes (in this case, an `sync_schema_props_perms` command has to |
|
342 be issued in a CubicWeb shell). |
|
343 |
|
344 |
|
345 Use of RQL expression for write permissions |
|
346 ``````````````````````````````````````````` |
|
347 It is possible to define RQL expression to provide update permission |
|
348 (`add`, `delete` and `update`) on relation and entity types. |
|
349 |
|
350 RQL expression for entity type permission: |
|
351 |
|
352 * you have to use the class `ERQLExpression` |
|
353 |
|
354 * the used expression corresponds to the WHERE statement of an RQL query |
|
355 |
|
356 * in this expression, the variables `X` and `U` are pre-defined references |
|
357 respectively on the current entity (on which the action is verified) and |
|
358 on the user who send the request |
|
359 |
|
360 * it is possible to use, in this expression, a special relation |
|
361 "has_<ACTION>_permission" where the subject is the user and the |
|
362 object is any variable, meaning that the user needs to have |
|
363 permission to execute the action <ACTION> on the entities related |
|
364 to this variable |
|
365 |
|
366 For RQL expressions on a relation type, the principles are the same except |
|
367 for the following: |
|
368 |
|
369 * you have to use the class `RRQLExpression` in the case of a non-final relation |
|
370 |
|
371 * in the expression, the variables `S`, `O` and `U` are pre-defined references |
|
372 to respectively the subject and the object of the current relation (on |
|
373 which the action is being verified) and the user who executed the query |
|
374 |
|
375 * we can also define rights over attributes of an entity (non-final relation), |
|
376 knowing that: |
|
377 |
|
378 - to define RQL expression, we have to use the class `ERQLExpression` |
|
379 in which `X` represents the entity the attribute belongs to |
|
380 |
|
381 - the permissions `add` and `delete` are equivalent. Only `add`/`read` |
|
382 are actually taken in consideration. |
|
383 |
|
384 .. note:: |
|
385 |
|
386 Potentially, the `use of an RQL expression to add an entity or a |
|
387 relation` can cause problems for the user interface, because if the |
|
388 expression uses the entity or the relation to create, then we are |
|
389 not able to verify the permissions before we actually add the entity |
|
390 (please note that this is not a problem for the RQL server at all, |
|
391 because the permissions checks are done after the creation). In such |
|
392 case, the permission check methods (CubicWebEntitySchema.check_perm |
|
393 and has_perm) can indicate that the user is not allowed to create |
|
394 this entity but can obtain the permission. To compensate this |
|
395 problem, it is usually necessary, for such case, to use an action |
|
396 that reflects the schema permissions but which enables to check |
|
397 properly the permissions so that it would show up if necessary. |
|
398 |
|
399 |
|
400 Use of RQL expression for reading rights |
|
401 ```````````````````````````````````````` |
|
402 |
|
403 The principles are the same but with the following restrictions: |
|
404 |
|
405 * we can not use `RRQLExpression` on relation types for reading |
|
406 |
|
407 * special relations "has_<ACTION>_permission" can not be used |
|
408 |
|
409 |
|
410 |
|
411 |
|
412 Defining your schema using yams |
|
413 ------------------------------- |
|
414 |
|
415 Entity type definition |
|
416 ~~~~~~~~~~~~~~~~~~~~~~ |
|
417 |
|
418 An entity type is defined by a Python class which inherits from |
|
419 :class:`yams.buildobjs.EntityType`. The class definition contains the |
|
420 description of attributes and relations for the defined entity type. |
|
421 The class name corresponds to the entity type name. It is expected to |
|
422 be defined in the module ``mycube.schema``. |
|
423 |
|
424 :Note on schema definition: |
|
425 |
|
426 The code in ``mycube.schema`` is not meant to be executed. The class |
|
427 EntityType mentioned above is different from the EntitySchema class |
|
428 described in the previous chapter. EntityType is a helper class to |
|
429 make Entity definition easier. Yams will process EntityType classes |
|
430 and create EntitySchema instances from these class definitions. Similar |
|
431 manipulation happen for relations. |
|
432 |
|
433 When defining a schema using python files, you may use the following shortcuts: |
|
434 |
|
435 - `required`: boolean indicating if the attribute is required, ed subject cardinality is '1' |
|
436 |
|
437 - `vocabulary`: specify static possible values of an attribute |
|
438 |
|
439 - `maxsize`: integer providing the maximum size of a string (no limit by default) |
|
440 |
|
441 For example: |
|
442 |
|
443 .. sourcecode:: python |
|
444 |
|
445 class Person(EntityType): |
|
446 """A person with the properties and the relations necessary for my |
|
447 application""" |
|
448 |
|
449 last_name = String(required=True, fulltextindexed=True) |
|
450 first_name = String(required=True, fulltextindexed=True) |
|
451 title = String(vocabulary=('Mr', 'Mrs', 'Miss')) |
|
452 date_of_birth = Date() |
|
453 works_for = SubjectRelation('Company', cardinality='?*') |
|
454 |
|
455 |
|
456 The entity described above defines three attributes of type String, |
|
457 last_name, first_name and title, an attribute of type Date for the date of |
|
458 birth and a relation that connects a `Person` to another entity of type |
|
459 `Company` through the semantic `works_for`. |
|
460 |
|
461 :Naming convention: |
|
462 |
|
463 Entity class names must start with an uppercase letter. The common |
|
464 usage is to use ``CamelCase`` names. |
|
465 |
|
466 Attribute and relation names must start with a lowercase letter. The |
|
467 common usage is to use ``underscore_separated_words``. Attribute and |
|
468 relation names starting with a single underscore are permitted, to |
|
469 denote a somewhat "protected" or "private" attribute. |
|
470 |
|
471 In any case, identifiers starting with "CW" or "cw" are reserved for |
|
472 internal use by the framework. |
|
473 |
|
474 |
|
475 The name of the Python attribute corresponds to the name of the attribute |
|
476 or the relation in *CubicWeb* application. |
|
477 |
|
478 An attribute is defined in the schema as follows:: |
|
479 |
|
480 attr_name = attr_type(properties) |
|
481 |
|
482 where `attr_type` is one of the type listed above and `properties` is |
|
483 a list of the attribute needs to satisfy (see `Properties`_ |
|
484 for more details). |
|
485 |
|
486 * it is possible to use the attribute `meta` to flag an entity type as a `meta` |
|
487 (e.g. used to describe/categorize other entities) |
|
488 |
|
489 .. XXX the paragraph below needs clarification and / or moving out in |
|
490 .. another place |
|
491 |
|
492 *Note*: if you end up with an `if` in the definition of your entity, this probably |
|
493 means that you need two separate entities that implement the `ITree` interface and |
|
494 get the result from `.children()` which ever entity is concerned. |
|
495 |
|
496 Inheritance |
|
497 ``````````` |
|
498 XXX feed me |
|
499 |
|
500 |
|
501 Definition of relations |
|
502 ~~~~~~~~~~~~~~~~~~~~~~~ |
|
503 |
|
504 XXX add note about defining relation type / definition |
|
505 |
|
506 A relation is defined by a Python class heriting `RelationType`. The name |
|
507 of the class corresponds to the name of the type. The class then contains |
|
508 a description of the properties of this type of relation, and could as well |
|
509 contain a string for the subject and a string for the object. This allows to create |
|
510 new definition of associated relations, (so that the class can have the |
|
511 definition properties from the relation) for example :: |
|
512 |
|
513 class locked_by(RelationType): |
|
514 """relation on all entities indicating that they are locked""" |
|
515 inlined = True |
|
516 cardinality = '?*' |
|
517 subject = '*' |
|
518 object = 'CWUser' |
|
519 |
|
520 If provided, the `subject` and `object` attributes denote the subject |
|
521 and object of the various relation definitions related to the relation |
|
522 type. Allowed values for these attributes are: |
|
523 |
|
524 * a string corresponding to an entity type |
|
525 * a tuple of string corresponding to multiple entity types |
|
526 * special string such as follows: |
|
527 |
|
528 - "**": all types of entities |
|
529 - "*": all types of non-meta entities |
|
530 - "@": all types of meta entities but not system entities (e.g. used for |
|
531 the basic schema description) |
|
532 |
|
533 When a relation is not inlined and not symmetrical, and it does not require |
|
534 specific permissions, it can be defined using a `SubjectRelation` |
|
535 attribute in the EntityType class. The first argument of `SubjectRelation` gives |
|
536 the entity type for the object of the relation. |
|
537 |
|
538 :Naming convention: |
|
539 |
|
540 Although this way of defining relations uses a Python class, the |
|
541 naming convention defined earlier prevails over the PEP8 conventions |
|
542 used in the framework: relation type class names use |
|
543 ``underscore_separated_words``. |
|
544 |
|
545 :Historical note: |
|
546 |
|
547 It has been historically possible to use `ObjectRelation` which |
|
548 defines a relation in the opposite direction. This feature is soon to be |
|
549 deprecated and therefore should not be used in newly written code. |
|
550 |
|
551 :Future deprecation note: |
|
552 |
|
553 In an even more remote future, it is quite possible that the |
|
554 SubjectRelation shortcut will become deprecated, in favor of the |
|
555 RelationType declaration which offers some advantages in the context |
|
556 of reusable cubes. |
|
557 |
|
558 Definition of permissions |
|
559 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
560 The entity type `CWPermission` from the standard library |
|
561 allows to build very complex and dynamic security architectures. The schema of |
|
562 this entity type is as follow: |
|
563 |
|
564 .. sourcecode:: python |
|
565 |
|
566 class CWPermission(EntityType): |
|
567 """entity type that may be used to construct some advanced security configuration |
|
568 """ |
|
569 name = String(required=True, indexed=True, internationalizable=True, maxsize=100) |
|
570 require_group = SubjectRelation('CWGroup', cardinality='+*', |
|
571 description=_('groups to which the permission is granted')) |
|
572 require_state = SubjectRelation('State', |
|
573 description=_("entity's state in which the permission is applicable")) |
|
574 # can be used on any entity |
|
575 require_permission = ObjectRelation('**', cardinality='*1', composite='subject', |
|
576 description=_("link a permission to the entity. This " |
|
577 "permission should be used in the security " |
|
578 "definition of the entity's type to be useful.")) |
|
579 |
|
580 |
|
581 Example of configuration: |
|
582 |
|
583 .. sourcecode:: python |
|
584 |
|
585 class Version(EntityType): |
|
586 """a version is defining the content of a particular project's release""" |
|
587 |
|
588 __permissions__ = {'read': ('managers', 'users', 'guests',), |
|
589 'update': ('managers', 'logilab', 'owners',), |
|
590 'delete': ('managers', ), |
|
591 'add': ('managers', 'logilab', |
|
592 ERQLExpression('X version_of PROJ, U in_group G,' |
|
593 'PROJ require_permission P, P name "add_version",' |
|
594 'P require_group G'),)} |
|
595 |
|
596 |
|
597 class version_of(RelationType): |
|
598 """link a version to its project. A version is necessarily linked to one and only one project. |
|
599 """ |
|
600 __permissions__ = {'read': ('managers', 'users', 'guests',), |
|
601 'delete': ('managers', ), |
|
602 'add': ('managers', 'logilab', |
|
603 RRQLExpression('O require_permission P, P name "add_version",' |
|
604 'U in_group G, P require_group G'),) |
|
605 } |
|
606 inlined = True |
|
607 |
|
608 |
|
609 This configuration indicates that an entity `CWPermission` named |
|
610 "add_version" can be associated to a project and provides rights to create |
|
611 new versions on this project to specific groups. It is important to notice that: |
|
612 |
|
613 * in such case, we have to protect both the entity type "Version" and the relation |
|
614 associating a version to a project ("version_of") |
|
615 |
|
616 * because of the genericity of the entity type `CWPermission`, we have to execute |
|
617 a unification with the groups and/or the states if necessary in the expression |
|
618 ("U in_group G, P require_group G" in the above example) |
|
619 |
|
620 |
|
621 |
|
622 Handling schema changes |
|
623 ~~~~~~~~~~~~~~~~~~~~~~~ |
|
624 |
|
625 Also, it should be clear that to properly handle data migration, an |
|
626 instance's schema is stored in the database, so the python schema file |
|
627 used to defined it is only read when the instance is created or |
|
628 upgraded. |
|
629 |
|
630 .. XXX complete me |
|