doc/book/en/devrepo/repo/hooks.rst
author Stephanie Marcu <stephanie.marcu@logilab.fr>
Fri, 24 Sep 2010 18:38:24 +0200
branchstable
changeset 6344 0cb0d8d83e4c
parent 6152 6824f8b61098
child 6366 1806148d6ce8
permissions -rw-r--r--
[book] add a link in hooks section to test, talk about create_entity in tests section
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
     1
.. -*- coding: utf-8 -*-
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
     2
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
     3
.. _hooks:
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
     4
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
     5
Hooks and Operations
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
     6
====================
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
     7
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
     8
Generalities
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
     9
------------
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
    10
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    11
Paraphrasing the `emacs`_ documentation, let us say that hooks are an
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    12
important mechanism for customizing an application. A hook is
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    13
basically a list of functions to be called on some well-defined
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    14
occasion (this is called `running the hook`).
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    15
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    16
.. _`emacs`: http://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    17
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    18
In CubicWeb, hooks are subclasses of the Hook class in
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    19
`server/hook.py`, implementing their own `call` method, and selected
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    20
over a set of pre-defined `events` (and possibly more conditions,
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    21
hooks being selectable AppObjects like views and components).
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    22
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    23
There are two families of events: data events and server events. In a
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    24
typical application, most of the Hooks are defined over data
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    25
events.
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
    26
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    27
The purpose of data hooks is to complement the data model as defined
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    28
in the schema.py, which is static by nature, with dynamic or value
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    29
driven behaviours. It is functionally equivalent to a `database
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    30
trigger`_, except that database triggers definition languages are not
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    31
standardized, hence not portable (for instance, PL/SQL works with
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    32
Oracle and PostgreSQL but not SqlServer nor Sqlite).
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    33
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    34
.. _`database trigger`: http://en.wikipedia.org/wiki/Database_trigger
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    35
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    36
Data hooks can serve the following purposes:
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    37
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    38
* enforcing constraints that the static schema cannot express
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    39
  (spanning several entities/relations, exotic value ranges and
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    40
  cardinalities, etc.)
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
    41
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    42
* implement computed attributes
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    43
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
    44
Operations are Hook-like objects that may be created by Hooks and
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    45
scheduled to happen just before (or after) the `commit` event. Hooks
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    46
being fired immediately on data operations, it is sometime necessary
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    47
to delay the actual work down to a time where all other Hooks have
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    48
run, for instance a validation check which needs that all relations be
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    49
already set on an entity. Also while the order of execution of Hooks
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    50
is data dependant (and thus hard to predict), it is possible to force
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
    51
an order on Operations.
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    52
5221
b851558456bb [doc/book] remove merged chapter
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5220
diff changeset
    53
Operations also may be used to process various side effects associated
b851558456bb [doc/book] remove merged chapter
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5220
diff changeset
    54
with a transaction such as filesystem udpates, mail notifications,
b851558456bb [doc/book] remove merged chapter
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5220
diff changeset
    55
etc.
b851558456bb [doc/book] remove merged chapter
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5220
diff changeset
    56
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    57
Operations are subclasses of the Operation class in `server/hook.py`,
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    58
implementing `precommit_event` and other standard methods (wholly
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
    59
described in :ref:`operations_api`).
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    60
6344
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
    61
.. hint::
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
    62
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
    63
   It is a good practice, to write unit tests for each hook. See an example in :ref:`hook_test`
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
    64
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    65
Events
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    66
------
2172
cf8f9180e63e delete-trailing-whitespace
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 1714
diff changeset
    67
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    68
Hooks are mostly defined and used to handle `dataflow`_ operations. It
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    69
means as data gets in (entities added, updated, relations set or
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    70
unset), specific events are issued and the Hooks matching these events
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
    71
are called.
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    72
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    73
.. _`dataflow`: http://en.wikipedia.org/wiki/Dataflow
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    74
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    75
Below comes a list of the dataflow events related to entities operations:
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    76
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    77
* before_add_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    78
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    79
* before_update_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    80
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    81
* before_delete_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    82
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    83
* after_add_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    84
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    85
* after_update_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    86
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    87
* after_delete_entity
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    88
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    89
These define ENTTIES HOOKS. RELATIONS HOOKS are defined
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    90
over the following events:
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    91
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    92
* after_add_relation
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
    93
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    94
* after_delete_relation
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    95
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    96
* before_add_relation
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    97
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    98
* before_delete_relation
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
    99
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   100
This is an occasion to remind us that relations support the add/delete
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   101
operation, but no update.
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   102
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   103
Non data events also exist. These are called SYSTEM HOOKS.
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   104
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   105
* server_startup
2172
cf8f9180e63e delete-trailing-whitespace
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 1714
diff changeset
   106
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   107
* server_shutdown
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   108
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   109
* server_maintenance
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   110
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   111
* server_backup
2172
cf8f9180e63e delete-trailing-whitespace
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents: 1714
diff changeset
   112
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   113
* server_restore
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   114
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   115
* session_open
1714
a721966779be new book layout, do not compile yet
sylvain.thenault@logilab.fr
parents:
diff changeset
   116
5191
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   117
* session_close
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   118
6d182c7d4392 [doc/book] begin chapter on Hooks/Operations
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 2172
diff changeset
   119
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   120
Using dataflow Hooks
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   121
--------------------
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   122
5231
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   123
Dataflow hooks either automate data operations or maintain the
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   124
consistency of the data model. In the later case, we must use a
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   125
specific exception named ValidationError
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   126
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   127
Validation Errors
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   128
~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   129
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   130
When a condition is not met in a Hook/Operation, it must raise a
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   131
`ValidationError`. Raising anything but a (subclass of)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   132
ValidationError is a programming error. Raising a ValidationError
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   133
entails aborting the current transaction.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   134
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   135
The ValidationError exception is used to convey enough information up
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   136
to the user interface. Hence its constructor is different from the
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   137
default Exception constructor. It accepts, positionally:
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   138
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   139
* an entity eid,
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   140
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   141
* a dict whose keys represent attribute (or relation) names and values
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   142
  an end-user facing message (hence properly translated) relating the
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   143
  problem.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   144
5231
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   145
An entity hook
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   146
~~~~~~~~~~~~~~
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   147
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   148
We will use a very simple example to show hooks usage. Let us start
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   149
with the following schema.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   150
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   151
.. sourcecode:: python
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   152
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   153
   class Person(EntityType):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   154
       age = Int(required=True)
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   155
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   156
We would like to add a range constraint over a person's age. Let's
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   157
write an hook. It shall be placed into mycube/hooks.py. If this file
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   158
were to grow too much, we can easily have a mycube/hooks/... package
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   159
containing hooks in various modules.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   160
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   161
.. sourcecode:: python
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   162
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   163
   from cubicweb import ValidationError
6152
6824f8b61098 use is_instance in a number of places (esp. documentation) rather than the deprecated implements
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents: 5394
diff changeset
   164
   from cubicweb.selectors import is_instance
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   165
   from cubicweb.server.hook import Hook
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   166
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   167
   class PersonAgeRange(Hook):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   168
        __regid__ = 'person_age_range'
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   169
        events = ('before_add_entity', 'before_update_entity')
6152
6824f8b61098 use is_instance in a number of places (esp. documentation) rather than the deprecated implements
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents: 5394
diff changeset
   170
        __select__ = Hook.__select__ & is_instance('Person')
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   171
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   172
        def __call__(self):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   173
            if 0 >= self.entity.age <= 120:
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   174
               return
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   175
            msg = self._cw._('age must be between 0 and 120')
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   176
            raise ValidationError(self.entity.eid, {'age': msg})
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   177
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   178
Hooks being AppObjects like views, they have a __regid__ and a
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   179
__select__ class attribute. The base __select__ is augmented with an
6152
6824f8b61098 use is_instance in a number of places (esp. documentation) rather than the deprecated implements
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents: 5394
diff changeset
   180
`is_instance` selector matching the desired entity type. The `events`
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   181
tuple is used by the Hook.__select__ base selector to dispatch the
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   182
hook on the right events. In an entity hook, it is possible to
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   183
dispatch on any entity event (e.g. 'before_add_entity',
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   184
'before_update_entity') at once if needed.
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   185
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   186
Like all appobjects, hooks have the `self._cw` attribute which
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   187
represents the current session. In entity hooks, a `self.entity`
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   188
attribute is also present.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   189
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   190
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   191
A relation hook
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   192
~~~~~~~~~~~~~~~
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   193
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   194
Let us add another entity type with a relation to person (in
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   195
mycube/schema.py).
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   196
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   197
.. sourcecode:: python
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   198
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   199
   class Company(EntityType):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   200
        name = String(required=True)
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   201
        boss = SubjectRelation('Person', cardinality='1*')
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   203
We would like to constrain the company's bosses to have a minimum
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   204
(legal) age. Let's write an hook for this, which will be fired when
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   205
the `boss` relation is established.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   206
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   207
.. sourcecode:: python
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   208
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   209
   class CompanyBossLegalAge(Hook):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   210
        __regid__ = 'company_boss_legal_age'
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   211
        events = ('before_add_relation',)
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   212
        __select__ = Hook.__select__ & match_rtype('boss')
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   213
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   214
        def __call__(self):
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   215
            boss = self._cw.entity_from_eid(self.eidto)
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   216
            if boss.age < 18:
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   217
                msg = self._cw._('the minimum age for a boss is 18')
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   218
                raise ValidationError(self.eidfrom, {'boss': msg})
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   219
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   220
We use the `match_rtype` selector to select the proper relation type.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   221
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   222
The essential difference with respect to an entity hook is that there
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   223
is no self.entity, but `self.eidfrom` and `self.eidto` hook attributes
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   224
which represent the subject and object eid of the relation.
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   225
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   226
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   227
Using Operations
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   228
----------------
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   229
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   230
Let's augment our example with a new `subsidiary_of` relation on Company.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   231
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   232
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   233
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   234
   class Company(EntityType):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   235
        name = String(required=True)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   236
        boss = SubjectRelation('Person', cardinality='1*')
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   237
        subsidiary_of = SubjectRelation('Company', cardinality='*?')
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   238
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   239
Base example
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   240
~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   241
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   242
We would like to check that there is no cycle by the `subsidiary_of`
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   243
relation. This is best achieved in an Operation since all relations
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   244
are likely to be set at commit time.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   245
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   246
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   247
6344
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
   248
    from cubicweb.server.hook import Hook, Operation, match_rtype
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
   249
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   250
    def check_cycle(self, session, eid, rtype, role='subject'):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   251
        parents = set([eid])
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   252
        parent = session.entity_from_eid(eid)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   253
        while parent.related(rtype, role):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   254
            parent = parent.related(rtype, role)[0]
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   255
            if parent.eid in parents:
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   256
                msg = session._('detected %s cycle' % rtype)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   257
                raise ValidationError(eid, {rtype: msg})
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   258
            parents.add(parent.eid)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   259
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   260
    class CheckSubsidiaryCycleOp(Operation):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   261
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   262
        def precommit_event(self):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   263
            check_cycle(self.session, self.eidto, 'subsidiary_of')
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   264
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   265
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   266
    class CheckSubsidiaryCycleHook(Hook):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   267
        __regid__ = 'check_no_subsidiary_cycle'
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   268
        events = ('after_add_relation',)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   269
        __select__ = Hook.__select__ & match_rtype('subsidiary_of')
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   270
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   271
        def __call__(self):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   272
            CheckSubsidiaryCycleOp(self._cw, eidto=self.eidto)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   273
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   274
The operation is instantiated in the Hook.__call__ method.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   275
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   276
An operation always takes a session object as first argument
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   277
(accessible as `.session` from the operation instance), and optionally
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   278
all keyword arguments needed by the operation. These keyword arguments
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   279
will be accessible as attributes from the operation instance.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   280
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   281
Like in Hooks, ValidationError can be raised in Operations. Other
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   282
exceptions are programming errors.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   283
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   284
Notice how our hook will instantiate an operation each time the Hook
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   285
is called, i.e. each time the `subsidiary_of` relation is set.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   286
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   287
Using set_operation
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   288
~~~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   289
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   290
There is an alternative method to schedule an Operation from a Hook,
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   291
using the `set_operation` function.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   292
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   293
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   294
5231
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   295
   from cubicweb.server.hook import set_operation
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   296
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   297
   class CheckSubsidiaryCycleHook(Hook):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   298
       __regid__ = 'check_no_subsidiary_cycle'
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   299
       events = ('after_add_relation',)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   300
       __select__ = Hook.__select__ & match_rtype('subsidiary_of')
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   301
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   302
       def __call__(self):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   303
           set_operation(self._cw, 'subsidiary_cycle_detection', self.eidto,
5231
40f53867e332 [doc/book] misc fixes/enhancements & notes
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5221
diff changeset
   304
                         CheckSubsidiaryCycleOp, rtype=self.rtype)
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   305
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   306
   class CheckSubsidiaryCycleOp(Operation):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   307
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   308
       def precommit_event(self):
6344
0cb0d8d83e4c [book] add a link in hooks section to test, talk about create_entity in tests section
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents: 6152
diff changeset
   309
           for eid in self.session.transaction_data['subsidiary_cycle_detection']:
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   310
               check_cycle(self.session, eid, self.rtype)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   311
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   312
Here, we call set_operation with a session object, a specially forged
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   313
key, a value that is the actual payload of an individual operation (in
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   314
our case, the object of the subsidiary_of relation) , the class of the
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   315
Operation, and more optional parameters to give to the operation (here
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   316
the rtype which do not vary accross operations).
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   317
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   318
The body of the operation must then iterate over the values that have
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   319
been mapped in the transaction_data dictionary to the forged key.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   320
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   321
This mechanism is especially useful on two occasions (not shown in our
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   322
example):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   323
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   324
* massive data import (reduced memory consumption within a large
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   325
  transaction)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   326
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   327
* when several hooks need to instantiate the same operation (e.g. an
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   328
  entity and a relation hook).
5202
4a77da652759 [doc/book] more about hooks (simple examples with entities and relations)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5191
diff changeset
   329
5259
61505346e28f [doc/book] add a ref from hooks to adv tutorial
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5231
diff changeset
   330
.. note::
61505346e28f [doc/book] add a ref from hooks to adv tutorial
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5231
diff changeset
   331
61505346e28f [doc/book] add a ref from hooks to adv tutorial
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5231
diff changeset
   332
  A more realistic example can be found in the advanced tutorial
61505346e28f [doc/book] add a ref from hooks to adv tutorial
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5231
diff changeset
   333
  chapter :ref:`adv_tuto_security_propagation`.
61505346e28f [doc/book] add a ref from hooks to adv tutorial
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5231
diff changeset
   334
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   335
.. _operations_api:
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   336
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   337
Operation: a small API overview
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   338
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   339
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   340
.. autoclass:: cubicweb.server.hook.Operation
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   341
.. autoclass:: cubicweb.server.hook.LateOperation
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   342
.. autofunction:: cubicweb.server.hook.set_operation
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   343
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   344
Hooks writing rules
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   345
-------------------
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   346
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   347
Remainder
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   348
~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   349
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   350
Never, ever use the `entity.foo = 42` notation to update an entity. It
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   351
will not work.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   352
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   353
How to choose between a before and an after event ?
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   354
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   355
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   356
Before hooks give you access to the old attribute (or relation)
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   357
values. By definition the database is not yet updated in a before
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   358
hook.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   359
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   360
To access old and new values in an before_update_entity hook, one can
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   361
use the `server.hook.entity_oldnewvalue` function which returns a
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   362
tuple of the old and new values. This function takes an entity and an
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   363
attribute name as parameters.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   364
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   365
In a 'before_add|update_entity' hook the self.entity contains the new
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   366
values. One is allowed to further modify them before database
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   367
operations, using the dictionary notation.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   368
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   369
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   370
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   371
   self.entity['age'] = 42
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   372
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   373
This is because using self.entity.set_attributes(age=42) will
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   374
immediately update the database (which does not make sense in a
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   375
pre-database hook), and will trigger any existing
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   376
before_add|update_entity hook, thus leading to infinite hook loops or
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   377
such awkward situations.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   378
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   379
Beyond these specific cases, updating an entity attribute or relation
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   380
must *always* be done using `set_attributes` and `set_relations`
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   381
methods.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   382
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   383
(Of course, ValidationError will always abort the current transaction,
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   384
whetever the event).
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   385
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   386
Peculiarities of inlined relations
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   387
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   388
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   389
Some relations are defined in the schema as `inlined` (see
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   390
:ref:`RelationType` for details). In this case, they are inserted in
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   391
the database at the same time as entity attributes.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   392
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   393
Hence in the case of before_add_relation, such relations already exist
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   394
in the database.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   395
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   396
Edited attributes
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   397
~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   398
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   399
On udpates, it is possible to ask the `entity.edited_attributes`
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   400
variable whether one attribute has been updated.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   401
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   402
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   403
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   404
  if 'age' not in entity.edited_attribute:
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   405
      return
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   406
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   407
Deleted in transaction
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   408
~~~~~~~~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   409
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   410
The session object has a deleted_in_transaction method, which can help
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   411
writing deletion Hooks.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   412
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   413
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   414
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   415
   if self._cw.deleted_in_transaction(self.eidto):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   416
      return
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   417
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   418
Given this predicate, we can avoid scheduling an operation.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   419
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   420
Disabling hooks
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   421
~~~~~~~~~~~~~~~
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   422
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   423
It is sometimes convenient to disable some hooks. For instance to
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   424
avoid infinite Hook loops. One uses the `hooks_control` context
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   425
manager.
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   426
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   427
This can be controlled more finely through the `category` Hook class
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   428
attribute, which is a string.
5220
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   429
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   430
.. sourcecode:: python
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   431
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   432
   with hooks_control(self.session, self.session.HOOKS_ALLOW_ALL, <category>):
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   433
       # ... do stuff
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   434
42f854b6083d [doc/book] complete chapter on hooks & ops
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5202
diff changeset
   435
.. autoclass:: cubicweb.server.session.hooks_control
5318
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   436
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   437
The existing categories are: ``email``, ``syncsession``,
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   438
``syncschema``, ``bookmark``, ``security``, ``worfklow``,
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   439
``metadata``, ``notification``, ``integrity``, ``activeintegrity``.
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   440
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   441
Nothing precludes one to invent new categories and use the
81cd2540c7d2 [doc/book] talk about the existing categories
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents: 5259
diff changeset
   442
hooks_control context manager to filter them (in or out).