[ADD] doc: new documentation, with training tutorials, and new scaffolding
[odoo/odoo.git] / doc / howtos / backend.rst
1 .. queue:: backend/series
2
3 =======
4 Backend
5 =======
6
7 Build an Odoo module
8 ====================
9
10 Odoo uses a client/server architecture in which clients are web browsers
11 accessing the odoo server via RPC.
12
13 Business logic and extension is generally performed on the server side,
14 although supporting client features (e.g. new data representation such as
15 interactive maps) can be added to the client.
16
17 Both server and client extensions are packaged as *modules* which are
18 optionally loaded in a *database*.
19
20 Odoo modules can either add brand new business logic to an Odoo system, or
21 alter and extend existing business logic: a module can be created to add your
22 country's accounting rules to Odoo's generic accounting support, while the
23 next module adds support for real-time visualisation of a bus fleet.
24
25 Everything in Odoo thus starts and ends with modules.
26
27 Composition of a module
28 -----------------------
29
30 An Odoo module can contain a number of elements:
31
32 Business objects
33     declared as Python classes, these resources are automatically persisted
34     by Odoo based on their configuration
35
36 Data files
37     XML or CSV files declaring metadata (views or workflows), configuration
38     data (modules parameterization), demonstration data and more
39
40 Web controllers
41     Handle requests from web browsers
42
43 Static web data
44     Images, CSS or javascript files used by the web interface or website
45
46 Module structure
47 ----------------
48
49 Each module is a directory within a *module directory*. Module directories
50 are specified by using the :option:`--addons-path <odoo.py --addons-path>`
51 option.
52
53 .. tip::
54     :class: aphorism
55
56     most command-line options can also be set using :ref:`a configuration
57     file <reference/cmdline/config>`
58
59 An Odoo module is declared by its :ref:`manifest <reference/module/manifest>`. It
60 is mandatory and contains a single python dictionary declaring various
61 metadata for the module: the module's name and description, list of Odoo
62 modules required for this one to work properly, references to data files, …
63
64 The manifest's general structure is::
65
66     {
67         'name': "MyModule",
68         'version': '1.0',
69         'depends': ['base'],
70         'author': "Author Name",
71         'category': 'Category',
72         'description': """
73         Description text
74         """,
75         # data files always loaded at installation
76         'data': [
77             'mymodule_view.xml',
78         ],
79         # data files containing optionally loaded demonstration data
80         'demo': [
81             'demo_data.xml',
82         ],
83     }
84
85 A module is also a
86 `Python package <http://docs.python.org/2/tutorial/modules.html#packages>`_
87 with a ``__init__.py`` file, containing import instructions for various Python
88 files in the module.
89
90 For instance, if the module has a single ``mymodule.py`` file ``__init__.py``
91 might contain::
92
93     import mymodule
94
95 .. exercise:: Module creation
96
97     Create an empty module Open Academy, install it in Odoo.
98
99     .. only:: solutions
100
101         #. Create a new folder ``openacademy``
102         #. Create an empty ``openacademy/__init__.py`` file
103         #. Create an ``openacademy/__openerp__.py`` file
104
105         .. patch::
106
107 Object-Relational Mapping
108 -------------------------
109
110 A key component of Odoo is the :abbr:`ORM (Object-Relational Mapping)` layer.
111 This layer avoids having to write most :abbr:`SQL (Structured Query Language)`
112 by hand and provides extensibility and security services\ [#rawsql]_.
113
114 Business objects are declared as Python classes extending
115 :class:`~openerp.models.Model` which integrates them into the automated
116 persistence system.
117
118 Models can be configured by setting a number of attributes at their
119 definition. The most important attribute is
120 :attr:`~openerp.models.Model._name` which is required and defines the name for
121 the model in the Odoo system. Here is a minimally complete definition of a
122 model::
123
124     from openerp import models
125     class MinimalModel(models.Model):
126         _name = 'test.model'
127
128 Model fields
129 ------------
130
131 Fields are used to define what the model can store and where. Fields are
132 defined as attributes on the model class::
133
134     from openerp import models, fields
135
136     class LessMinimalModel(models.Model):
137         _name = 'test.model2'
138
139         name = fields.Char()
140
141 Common Attributes
142 #################
143
144 Much like the model itself, its fields can be configured, by passing
145 configuration attributes as parameters::
146
147     name = field.Char(required=True)
148
149 Some attributes are available on all fields, here are the most common ones:
150
151 :attr:`~openerp.fields.Field.string` (``unicode``, default: field's name)
152     The label of the field in UI (visible by users).
153 :attr:`~openerp.fields.Field.required` (``bool``, default: ``False``)
154     If ``True``, the field can not be empty, it must either have a default
155     value or always be given a value when creating a record.
156 :attr:`~openerp.fields.Field.help` (``unicode``, default: ``''``)
157     Long-formm, provides a help tooltip to users in the UI.
158 :attr:`~openerp.fields.Field.index` (``bool``, default: ``False``)
159     Requests that Odoo create a `database index`_ on the column
160
161 Simple fields
162 #############
163
164 There are two broad categories of fields: "simple" fields which are atomic
165 values stored directly in the model's table and "relational" fields linking
166 records (of the same model or of different models).
167
168 Example of simple fields are :class:`~openerp.fields.Boolean`,
169 :class:`~openerp.fields.Date`, :class:`~openerp.fields.Char`.
170
171 Reserved fields
172 ###############
173
174 Odoo creates a few fields in all models\ [#autofields]_. These fields are
175 managed by the system and shouldn't be written to. They can be read if
176 useful or necessary:
177
178 :attr:`~openerp.fields.Model.id` (:class:`~openerp.fields.Id`)
179     the unique identifier for a record in its model
180 :attr:`~openerp.fields.Model.create_date` (:class:`~openerp.fields.Datetime`)
181     creation date of the record
182 :attr:`~openerp.fields.Model.create_uid` (:class:`~openerp.fields.Many2one`)
183     user who created the record
184 :attr:`~openerp.fields.Model.write_date` (:class:`~openerp.fields.Datetime`)
185     last modification date of the record
186 :attr:`~openerp.fields.Model.write_uid` (:class:`~openerp.fields.Many2one`)
187     user who last modified the record
188
189 Special fields
190 ##############
191
192 By default, Odoo also requires a ``name`` field on all models for various
193 display and search behaviors. The field used for these purposes can be
194 overridden by setting :attr:`~openerp.models.Model._rec_name`.
195
196 .. exercise:: Define a model
197
198     Define a new data model *Course* in the *openacademy* module. A course
199     has a title and a description. Courses must have a title.
200
201     .. only:: solutions
202
203         #. Create a new file ``openacademy/course.py``
204         #. Edit ``openacademy/__init__.py`` to import it
205
206         .. patch::
207
208 Data files
209 ----------
210
211 Odoo is a highly data driven system. Although behavior is customized using
212 Python_ code part of a module's value is in the data it sets up when loaded.
213
214 .. tip:: some modules exist solely to add data into Odoo
215     :class: aphorism
216
217 Module data is declared via :ref:`data files <reference/data>`, XML files with
218 ``<record>`` elements. Each ``<record>`` element creates or updates a database
219 record.
220
221 .. code-block:: xml
222
223     <openerp>
224         <data>
225             <record model="{model name}" id="{record identifier}">
226                 <field name="{a field name}">{a value}</field>
227             </record>
228         </data>
229     <openerp>
230
231 * ``model`` is the name of the Odoo model for the record
232 * ``id`` is an :term:`external identifier`, it allows referring to the record
233   (without having to know its in-database identifier)
234 * ``<field>`` elements have a ``name`` which is the name of the field in the
235   model (e.g. ``description``). Their body is the field's value.
236
237 Data files have to be declared in the manifest file to be loaded, they can
238 be declared in the ``'data'`` list (always loaded) or in the ``'demo'`` list
239 (only loaded in demonstration mode).
240
241 .. exercise:: Define demonstration data
242
243     Create demonstration data filling the *Courses* model with a few
244     demonstration courses.
245
246     .. only:: solutions
247
248         #. Create a new file ``openacademy/demo.xml``
249         #. Add the file to the ``'demo'`` list of your ``__openerp__.py``
250
251         .. patch::
252
253 Actions and Menus
254 -----------------
255
256 Actions and menus are regular records in database, usually declared through
257 data files. Actions can be triggered in three ways:
258
259 #. by clicking on menu items (linked to specific actions)
260 #. by clicking on buttons in views (if these are connected to actions)
261 #. as contextual actions on object
262
263 Because menus are somewhat complex to declare there is a ``<menuitem>``
264 shortcut to declare an ``ir.ui.menu`` and connect it to the corresponding
265 action more easily.
266
267 .. code-block:: xml
268
269     <record model="ir.actions.act_window" id="action_list_ideas">
270         <field name="name">Ideas</field>
271         <field name="res_model">idea.idea</field>
272         <field name="view_mode">tree,form</field>
273     </record>
274     <menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
275               action="action_list_ideas"/>
276
277 .. danger::
278     :class: aphorism
279
280     The action must be declared before its corresponding menu in the XML file.
281
282     Data files are executed sequentially, the action's ``id`` must be present
283     in the database before the menu can be created.
284
285 .. exercise:: Define new menu entries
286
287     Define new menu entries to access courses and sessions under the
288     OpenAcademy menu entry. A user should be able to
289
290     - display a list of all the courses
291     - create/modify courses
292
293     .. only:: solutions
294
295         #. Create ``openacademy/views/openacademy.xml`` with an action and
296            the menus triggering the action
297         #. Add it to the ``data`` list of ``openacademy/__openerp__.py``
298
299         .. patch::
300
301 Basic views
302 ===========
303
304 Views define the way the records of a model are displayed. Each type of view
305 represents a mode of visualization (a list of records, a graph of their
306 aggregation, …). Views can either be requested generically via their type
307 (e.g. *a list of partners*) or specifically via their id. For generic
308 requests, the view with the correct type and the lowest priority will be
309 used (so the lowest-priority view of each type is the default view for that
310 type).
311
312 :ref:`View inheritance <reference/views/inheritance>` allows altering views
313 declared elsewhere (adding or removing content).
314
315 Generic view declaration
316 ------------------------
317
318 A view is declared as a record of the model ``ir.ui.view``. The view type
319 is implied by the root element of the ``arch`` field:
320
321 .. code-block:: xml
322
323     <record model="ir.ui.view" id="view_id">
324         <field name="name">view.name</field>
325         <field name="model">object_name</field>
326         <field name="priority" eval="16"/>
327         <field name="arch" type="xml">
328             <!-- view content: <form>, <tree>, <graph>, ... -->
329         </field>
330     </record>
331
332 .. danger:: The view's content is XML.
333     :class: aphorism
334
335     The ``arch`` field must thus be declared as ``type="xml"`` to be parsed
336     correctly.
337
338 Tree views
339 ----------
340
341 Tree views, also called list views, display records in a tabular form.
342
343 Their root element is ``<tree>``. The simplest form of the tree view simply
344 lists all the fields to display in the table (each field as a column):
345
346 .. code-block:: xml
347
348     <tree string="Idea list">
349         <field name="name"/>
350         <field name="inventor_id"/>
351     </tree>
352
353 Form views
354 ----------
355
356 Forms are used to create and edit single records.
357
358
359 Their root element is ``<form>``. They composed of high-level structure
360 elements (groups, notebooks) and interactive elements (buttons and fields):
361
362 .. code-block:: xml
363
364     <form string="Idea form">
365         <group colspan="4">
366             <group colspan="2" col="2">
367                 <separator string="General stuff" colspan="2"/>
368                 <field name="name"/>
369                 <field name="inventor_id"/>
370             </group>
371
372             <group colspan="2" col="2">
373                 <separator string="Dates" colspan="2"/>
374                 <field name="active"/>
375                 <field name="invent_date" readonly="1"/>
376             </group>
377
378             <notebook colspan="4">
379                 <page string="Description">
380                     <field name="description" nolabel="1"/>
381                 </page>
382             </notebook>
383
384             <field name="state"/>
385         </group>
386     </form>
387
388 .. exercise:: Customise form view using XML
389
390     Create your own form view for the Course object. Data displayed should be:
391     the name and the description of the course.
392
393     .. only:: solutions
394
395         .. patch::
396
397 .. exercise:: Notebooks
398
399     In the Course form view, put the description field under a tab, such that
400     it will be easier to add other tabs later, containing additional
401     information.
402
403     .. only:: solutions
404
405         Modify the Course form view as follows:
406
407         .. patch::
408
409 Form views can also use plain HTML for more flexible layouts:
410
411 .. code-block:: xml
412
413     <form string="Idea Form">
414         <header>
415             <button string="Confirm" type="object" name="action_confirm"
416                     states="draft" class="oe_highlight" />
417             <button string="Mark as done" type="object" name="action_done"
418                     states="confirmed" class="oe_highlight"/>
419             <button string="Reset to draft" type="object" name="action_draft"
420                     states="confirmed,done" />
421             <field name="state" widget="statusbar"/>
422         </header>
423         <sheet>
424             <div class="oe_title">
425                 <label for="name" class="oe_edit_only" string="Idea Name" />
426                 <h1><field name="name" /></h1>
427             </div>
428             <separator string="General" colspan="2" />
429             <group colspan="2" col="2">
430                 <field name="description" placeholder="Idea description..." />
431             </group>
432         </sheet>
433     </form>
434
435 Search views
436 ------------
437
438 Search views customize the search field associated with the list view (and
439 other aggregated views). Their root element is ``<search>`` and they're
440 composed of fields defining which fields can be searched on:
441
442 .. code-block:: xml
443
444     <search>
445         <field name="name"/>
446         <field name="inventor_id"/>
447     </search>
448
449 If no search view exists for the model, Odoo generates one which only allows
450 searching on the ``name`` field.
451
452 .. exercise:: Search courses
453
454     Allow searching for courses based on their title or their description.
455
456     .. only:: solutions
457
458         .. patch::
459
460 Relations between models
461 ========================
462
463 A record from a model may be related to a record from another model. For
464 instance, a sale order record is related to a client record that contains the
465 client data; it is also related to its sale order line records.
466
467 .. exercise:: Create a session model
468
469     For the module Open Academy, we consider a model for *sessions*: a session
470     is an occurrence of a course taught at a given time for a given audience.
471
472     Create a model for *sessions*. A session has a name, a start date, a
473     duration and a number of seats. Add an action and a menu item to display
474     them.
475
476     .. only:: solutions
477
478         Create class *Session*:
479
480         .. patch::
481
482         .. note:: ``digits=(6, 2)`` specifies the precision of a float number:
483                   6 is the total number of digits, while 2 is the number of
484                   digits after the comma. Note that it results in the number
485                   digits before the comma is a maximum 4
486
487 Relational fields
488 -----------------
489
490 Relational fields link records, either of the same model (hierarchies) or
491 between different models.
492
493 Relational field types are:
494
495 :class:`Many2one(other_model, ondelete='set null') <openerp.fields.Many2one>`
496     A simple link to an other object::
497
498         print foo.other_id.name
499
500     .. seealso:: `foreign keys <http://www.postgresql.org/docs/9.3/static/tutorial-fk.html>`_
501
502 :class:`One2many(other_model, related_field) <openerp.fields.One2many>`
503     A virtual relationship, inverse of a :class:`~openerp.fields.Many2one`.
504     A :class:`~openerp.fields.One2many` behaves as a container of records,
505     accessing it results in a (possibly empty) set of records::
506
507         for other in foo.other_ids:
508             print foo.name
509
510     .. danger::
511
512         Because a :class:`~openerp.fields.One2many` is a virtual relationship,
513         there *must* be a :class:`~openerp.fields.Many2one` field in the
514         :samp:`{other_model}`, and its name *must* be :samp:`{related_field}`
515
516 :class:`Many2many(other_model) <openerp.fields.Many2many>`
517     Bidirectional multiple relationship, any record on one side can be related
518     to any number of records on the other side. Behaves as a container of
519     records, accessing it also results in a possibly empty set of records::
520
521         for other in foo.other_ids:
522             print foo.name
523
524 .. exercise:: Many2one relations
525
526     Using a many2one, modify the *Course* and *Session* models to reflect their
527     relation with other models:
528
529     - A course has a *responsible* user; the value of that field is a record of
530       the built-in model ``res.users``.
531     - A session has an *instructor*; the value of that field is a record of the
532       built-in model ``res.partner``.
533     - A session is related to a *course*; the value of that field is a record
534       of the model ``openacademy.course`` and is required.
535
536     .. only:: solutions
537
538         #. Add the relevant ``Many2one`` fields to the models, and
539         #. add access to the session object in
540            ``openacademy/view/openacademy.xml``.
541
542         .. patch::
543
544 .. exercise:: Inverse one2many relations
545
546     Using the inverse relational field one2many, modify the models to reflect
547     the relation between courses and sessions.
548
549     .. only:: solutions
550
551         Modify the ``Course`` class as follows:
552
553         .. patch::
554
555 .. exercise:: Multiple many2many relations
556
557     Using the relational field many2many, modify the *Session* model to relate
558     every session to a set of *attendees*. Attendees will be represented by
559     partner records, so we will relate to the built-in model ``res.partner``.
560
561     .. only:: solutions
562
563         Modify the ``Session`` class as follows:
564
565         .. patch::
566
567 .. exercise:: Views modification
568
569     For the *Course* model,
570
571     * the name and instructor for the course should be displayed in the tree
572       view
573     * the form view should display the course name and responsible at
574       the top, followed by the course description in a tab and the course
575       sessions in a second tab
576
577     For the *Session* model,
578
579     * the name of the session and the session course should be displayed in
580       the tree view
581     * the form view should display all the session's fields
582
583     Try to lay out the form views so that they're clear and readable.
584
585     .. only:: solutions
586
587         .. patch::
588
589 Inheritance
590 ===========
591
592 Model inheritance
593 -----------------
594
595 Odoo provides two *inheritance* mechanisms to extend an existing model in a
596 modular way.
597
598 The first inheritance mechanism allows a module to modify the behavior of a
599 model defined in another module:
600
601 - add fields to a model,
602 - override the definition of fields on a model,
603 - add constraints to a model,
604 - add methods to a model,
605 - override existing methods on a model.
606
607 The second inheritance mechanism (delegation) allows to link every record of a
608 model to a record in a parent model, and provides transparent access to the
609 fields of the parent record.
610
611 .. image:: backend/inheritance_methods.png
612     :align: center
613
614 .. seealso::
615
616     * :attr:`~openerp.models.Model._inherit`
617     * :attr:`~openerp.models.Model._inherits`
618
619 View inheritance
620 ----------------
621
622 Instead of modifying existing views in place (by overwriting them), Odoo
623 provides view inheritance where children "extension" views are applied on top of
624 root views, and can add or remove content from their parent.
625
626 An extension view references its parent using the ``inherit_id`` field, and
627 instead of a single view its ``arch`` field is composed of any number of
628 ``xpath`` elements selecting and altering the content of their parent view:
629
630 .. code-block:: xml
631
632     <!-- improved idea categories list -->
633     <record id="idea_category_list2" model="ir.ui.view">
634         <field name="name">id.category.list2</field>
635         <field name="model">ir.ui.view</field>
636         <field name="inherit_id" ref="id_category_list"/>
637         <field name="arch" type="xml">
638             <!-- find field description inside tree, and add the field
639                  idea_ids after it -->
640             <xpath expr="/tree/field[@name='description']" position="after">
641               <field name="idea_ids" string="Number of ideas"/>
642             </xpath>
643         </field>
644     </record>
645
646 ``expr``
647     An XPath_ expression selecting a single element in the parent view.
648     Raises an error if it matches no element or more than one
649 ``position``
650     Operation to apply to the matched element:
651
652     ``inside``
653         appends ``xpath``'s body at the end of the matched element
654     ``replace``
655         replaces the matched element by the ``xpath``'s body
656     ``before``
657         inserts the ``xpath``'s body as a sibling before the matched element
658     ``after``
659         inserts the ``xpaths``'s body as a sibling after the matched element
660     ``attributes``
661         alters the attributes of the matched element using special
662         ``attribute`` elements in the ``xpath``'s body
663
664 .. exercise:: Alter existing content
665
666     * Using model inheritance, modify the existing *Partner* model to add an
667       ``instructor`` boolean field
668     * Using view inheritance, display this fields in the partner form view
669
670     .. only:: solutions
671
672        .. note::
673
674            This is the opportunity to introduce the developer mode to
675            inspect the view, find its external ID and the place to put the
676            new field.
677
678        #. Create a ``openacademy/partner.py`` and import it in
679           ``__init__.py``
680        #. Create an ``openacademy/views/partner.xml`` and add it to
681           ``__openerp__.py``
682
683        .. patch::
684
685 Domains
686 #######
687
688 In Odoo, :ref:`reference/orm/domains` are values that encode conditions on
689 records. A domain is a  list of criteria used to select a subset of a model's
690 records. Each criteria is a triple with a field name, an operator and a value.
691
692 For instance, when used on the *Product* model the following domain selects
693 all *services* with a unit price over *1000*::
694
695     [('product_type', '=', 'service'), ('unit_price', '>', 1000)]
696
697 By default criteria are combined with an implicit AND. The logical operators
698 ``&`` (AND), ``|`` (OR) and ``!`` (NOT) can be used to explicitly combine
699 criteria. They are used in prefix position (the operator is inserted before
700 its arguments rather than between). For instance to select products "which are
701 services *OR* have a unit price which is *NOT* between 1000 and 2000"::
702
703     ['|',
704         ('product_type', '=', 'service'),
705         '!', '&',
706             ('unit_price', '>=', 1000),
707             ('unit_price', '<', 2000)]
708
709 A ``domain`` parameter can be added to relational fields to limit valid
710 records for the relation when trying to select records in the client interface.
711
712 .. exercise:: Domains on relational fields
713
714     When selecting the instructor for a *Session*, only instructors (partners
715     with ``instructor`` set to ``True``) should be visible.
716
717     .. only:: solutions
718
719         .. patch::
720
721         .. note::
722
723             A domain declared as a literal list is evaluated server-side and
724             can't refer to dynamic values on the right-hand side, a domain
725             declared as a string is evaluated client-side and allows
726             field names on the right-hand side
727
728 .. exercise:: More complex domains
729
730     Create new partner categories *Teacher / Level 1* and *Teacher / Level 2*.
731     The instructor for a session can be either an instructor or a teacher
732     (of any level).
733
734     .. only:: solutions
735
736         #. Modify the *Session* model's domain
737         #. Modify ``openacademy/view/partner.xml`` to get access to
738            *Partner categories*:
739
740         .. patch::
741
742 Computed fields and default values
743 ==================================
744
745 So far fields have been stored directly in and retrieved directly from the
746 database. Fields can also be *computed*. In that case, the field's value is not
747 retrieved from the database but computed on-the-fly by calling a method of the
748 model.
749
750 To create a computed field, create a field and set its attribute
751 :attr:`~openerp.fields.Field.compute` to the name of a method. The computation
752 method should simply set the value of the field to compute on every record in
753 ``self``.
754
755 .. danger:: ``self`` is a collection
756     :class: aphorism
757
758     The object ``self`` is a *recordset*, i.e., an ordered collection of
759     records. It supports the standard Python operations on collections, like
760     ``len(self)`` and ``iter(self)``, plus extra set operations like ``recs1 +
761     recs2``.
762
763     Iterating over ``self`` gives the records one by one, where each record is
764     itself a collection of size 1. You can access/assign fields on single
765     records by using the dot notation, like ``record.name``.
766
767 .. code-block:: python
768
769     import random
770     from openerp import models, fields
771
772     class ComputedModel(models.Model):
773         _name = 'test.computed'
774
775         name = fields.Char(compute='_compute_name')
776
777         def _compute_name(self):
778             for record in self:
779                 record.name = str(random.randint(1, 1e6))
780
781 Our compute method is very simple: it loops over ``self`` and performs the same
782 operation on every record. We can make it slightly simpler by using the
783 decorator :func:`~openerp.api.one` to automatically loop on the collection::
784
785         @api.one
786         def _compute_name(self):
787             self.name = str(random.randint(1, 1e6))
788
789 Dependencies
790 ------------
791
792 The value of a computed field usually depends on the values of other fields on
793 the computed record. The ORM expects the developer to specify those dependencies
794 on the compute method with the decorator :func:`~openerp.api.depends`.
795 The given dependencies are used by the ORM to trigger the recomputation of the
796 field whenever some of its dependencies have been modified::
797
798     from openerp import models, fields, api
799
800     class ComputedModel(models.Model):
801         _name = 'test.computed'
802
803         name = fields.Char(compute='_compute_name')
804         value = fields.Integer()
805
806         @api.one
807         @api.depends('value')
808         def _compute_name(self):
809             self.name = "Record with value %s" % self.value
810
811 .. exercise:: Computed fields
812
813     * Add the percentage of taken seats to the *Session* model
814     * Display that field in the tree and form views
815     * Display the field as a progress bar
816
817     .. only:: solutions
818
819         #. Add a computed field to *Session*
820         #. Show the field in the *Session* view:
821
822         .. patch::
823
824 Default values
825 --------------
826
827 Any field can be given a default value. In the field definition, add the option
828 ``default=X`` where ``X`` is either a Python literal value (boolean, integer,
829 float, string), or a function taking a recordset and returning a value::
830
831     name = fields.Char(default="Unknown")
832     user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
833
834 .. exercise:: Active objects – Default values
835
836     * Define the start_date default value as today (see
837       :class:`~openerp.fields.Date`).
838     * Add a field ``active`` in the class Session, and set sessions as active by
839       default.
840
841     .. only:: solutions
842
843         .. patch::
844
845         .. note::
846
847             Odoo has built-in rules making fields with an ``active`` field set
848             to ``False`` invisible.
849
850 Onchange
851 ========
852
853 The "onchange" mechanism provides a way for the client interface to update a
854 form whenever the user has filled in a value in a field, without saving anything
855 to the database.
856
857 For instance, suppose a model has three fields ``amount``, ``unit_price`` and
858 ``price``, and you want to update the price on the form when any of the other
859 fields is modified. To achieve this, define a method where ``self`` represents
860 the record in the form view, and decorate it with :func:`~openerp.api.onchange`
861 to specify on which field it has to be triggered. Any change you make on
862 ``self`` will be reflected on the form.
863
864 .. code-block:: xml
865
866     <!-- content of form view -->
867     <field name="amount"/>
868     <field name="unit_price"/>
869     <field name="price" readonly="1"/>
870
871 .. code-block:: python
872
873     # onchange handler
874     @api.onchange('amount', 'unit_price')
875     def _onchange_price(self):
876         # set auto-changing field
877         self.price = self.amount * self.unit_price
878         # Can optionally return a warning and domains
879         return {
880             'warning': {
881                 'title': "Something bad happened",
882                 'message': "It was very bad indeed",
883             }
884         }
885
886 For computed fields, valued ``onchange`` behavior is built-in as can be seen by
887 playing with the *Session* form: change the number of seats or participants, and
888 the ``taken_seats`` progressbar is automatically updated.
889
890 .. exercise:: Warning
891
892     Add an explicit onchange to warn about invalid values, like a negative
893     number of seats, or more participants than seats.
894
895     .. only:: solutions
896
897         .. patch::
898
899 Model constraints
900 =================
901
902 Odoo provides two ways to set up automatically verified invariants:
903 :func:`Python constraints <openerp.api.constrains>` and
904 :attr:`SQL constaints <openerp.models.Model._sql_constraints>`.
905
906 A Python constraint is defined as a method decorated with
907 :func:`~openerp.api.constrains`, and invoked on a recordset. The decorator
908 specifies which fields are involved in the constraint, so that the constraint is
909 automatically evaluated when one of them is modified. The method is expected to
910 raise an exception if its invariant is not satisfied::
911
912     from openerp.exceptions import ValidationError
913
914     @api.constrains('age')
915     def _check_something(self):
916         for record in self:
917             if record.age > 20:
918                 raise ValidationError("Your record is too old: %s" % record.age)
919         # all records passed the test, don't return anything
920
921 .. exercise:: Add Python constraints
922
923     Add a constraint that checks that the instructor is not present in the
924     attendees of his/her own session.
925
926     .. only:: solutions
927
928         .. patch::
929
930 SQL constraints are defined through the model attribute
931 :attr:`~openerp.models.Model._sql_constraints`. The latter is assigned to a list
932 of triples of strings ``(name, sql_definition, message)``, where ``name`` is a
933 valid SQL constraint name, ``sql_definition`` is a table_constraint_ expression,
934 and ``message`` is the error message.
935
936 .. exercise:: Add SQL constraints
937
938     With the help of `PostgreSQL's documentation`_ , add the following
939     constraints:
940
941     #. CHECK that the course description and the course title are different
942     #. Make the Course's name UNIQUE
943
944     .. only:: solutions
945
946         .. patch::
947
948 .. exercise:: Exercise 6 - Add a duplicate option
949
950     Since we added a constraint for the Course name uniqueness, it is not
951     possible to use the "duplicate" function anymore (:menuselection:`Form -->
952     Duplicate`).
953
954     Re-implement your own "copy" method which allows to duplicate the Course
955     object, changing the original name into "Copy of [original name]".
956
957     .. only:: solutions
958
959         .. patch::
960
961 Advanced Views
962 ==============
963
964 Tree views
965 ----------
966
967 Tree views can take supplementary attributes to further customize their
968 behavior:
969
970 ``colors``
971     mappings of colors to conditions. If the condition evaluates to ``True``,
972     the corresponding color is applied to the row:
973
974     .. code-block:: xml
975
976         <tree string="Idea Categories" colors="blue:state=='draft';red:state=='trashed'">
977             <field name="name"/>
978             <field name="state"/>
979         </tree>
980
981     Clauses are separated by ``;``, the color and condition are separated by
982     ``:``.
983
984 ``editable``
985     Either ``"top"`` or ``"bottom"``. Makes the tree view editable in-place
986     (rather than having to go through the form view), the value is the
987     position where new rows appear.
988
989 .. exercise:: List coloring
990
991     Modify the Session tree view in such a way that sessions lasting less than
992     5 days are colored blue, and the ones lasting more than 15 days are
993     colored red.
994
995     .. only:: solutions
996
997         Modify the session tree view:
998
999         .. patch::
1000
1001 Calendars
1002 ---------
1003
1004 Displays records as calendar events. Their root element is ``<calendar>`` and
1005 their most common attributes are:
1006
1007 ``color``
1008     The name of the field used for *color segmentation*. Colors are
1009     automatically distributed to events, but events in the same color segment
1010     (records which have the same value for their ``@color`` field) will be
1011     given the same color.
1012 ``date_start``
1013     record's field holding the start date/time for the event
1014 ``date_stop`` (optional)
1015     record's field holding the end date/time for the event
1016
1017 field (to define the label for each calendar event)
1018
1019 .. code-block:: xml
1020
1021     <calendar string="Ideas" date_start="invent_date" color="inventor_id">
1022         <field name="name"/>
1023     </calendar>
1024
1025 .. exercise:: Calendar view
1026
1027     Add a Calendar view to the *Session* model enabling the user to view the
1028     events associated to the Open Academy.
1029
1030     .. only:: solutions
1031
1032         #. Add an ``end_date`` field computed from ``start_date`` and
1033            ``duration``
1034
1035            .. tip:: the inverse function makes the field writable, and allows
1036                     moving the sessions (via drag and drop) in the calendar view
1037
1038         #. Add a calendar view to the *Session* model
1039         #. And add the calendar view to the *Session* model's actions
1040
1041         .. patch::
1042
1043 Search views
1044 ------------
1045
1046 Search view fields can take custom operators or :ref:`reference/orm/domains`
1047 for more flexible matching of results.
1048
1049 Search views can also contain *filters* which act as toggles for predefined
1050 searches (defined using :ref:`reference/orm/domains`):
1051
1052 .. code-block:: xml
1053
1054     <search string="Ideas">
1055         <filter name="my_ideas" domain="[('inventor_id','=',uid)]"
1056                 string="My Ideas" icon="terp-partner"/>
1057         <field name="name"/>
1058         <field name="description"/>
1059         <field name="inventor_id"/>
1060         <field name="country_id" widget="selection"/>
1061     </search>
1062
1063 To use a non-default search view in an action, it should be linked using the
1064 ``search_view_id`` field of the action record.
1065
1066 The action can also set default values for search fields through its
1067 ``context`` field: context keys of the form
1068 :samp:`search_default_{field_name}` will initialize *field_name* with the
1069 provided value. Search filters must have an optional ``@name`` to have a
1070 default and behave as booleans (they can only be enabled by default).
1071
1072 .. exercise:: Search views
1073
1074     Add a button to filter the courses for which the current user is the
1075     responsible in the course search view. Make it selected by default.
1076
1077     .. only:: solutions
1078
1079         .. patch::
1080
1081 Gantt
1082 -----
1083
1084 Horizontal bar charts typically used to show project planning and advancement,
1085 their root element is ``<gantt>``.
1086
1087 .. code-block:: xml
1088
1089     <gantt string="Ideas" date_start="invent_date" color="inventor_id">
1090         <level object="idea.idea" link="id" domain="[]">
1091             <field name="inventor_id"/>
1092         </level>
1093     </gantt>
1094
1095 .. exercise:: Gantt charts
1096
1097     Add a Gantt Chart enabling the user to view the sessions scheduling linked
1098     to the Open Academy module. The sessions should be grouped by instructor.
1099
1100     .. only:: solutions
1101
1102         #. Create a computed field expressing the session's duration in hours
1103         #. Add the gantt view's definition, and add the gantt view to the
1104            *Session* model's action
1105
1106         .. patch::
1107
1108 Graph views
1109 -----------
1110
1111 Graph views allow aggregated overview and analysis of models, their root
1112 element is ``<graph>``.
1113
1114 Graph views have 4 display modes, the default mode is selected using the
1115 ``@type`` attribute.
1116
1117 Pivot
1118     a multidimensional table, allows the selection of filers and dimensions
1119     to get the right aggregated dataset before moving to a more graphical
1120     overview
1121 Bar (default)
1122     a bar chart, the first dimension is used to define groups on the
1123     horizontal axis, other dimensions define aggregated bars within each group.
1124
1125     By default bars are side-by-side, they can be stacked by using
1126     ``@stacked="True"`` on the ``<graph>``
1127 Line
1128     2-dimensional line chart
1129 Pie
1130     2-dimensional pie
1131
1132 Graph views contain ``<field>`` with a mandatory ``@type`` attribute taking
1133 the values:
1134
1135 ``row`` (default)
1136     the field should be aggregated by default
1137 ``measure``
1138     the field should be aggregated rather than grouped on
1139
1140 .. code-block:: xml
1141
1142     <graph string="Total idea score by Inventor">
1143         <field name="inventor_id"/>
1144         <field name="score" type="measure"/>
1145     </graph>
1146
1147 .. warning::
1148
1149     Graph views perform aggregations on database values, they do not work
1150     with non-stored computed fields.
1151
1152 .. exercise:: Graph view
1153
1154     Add a Graph view in the Session object that displays, for each course, the
1155     number of attendees under the form of a bar chart.
1156
1157     .. only:: solutions
1158
1159         #. Add the number of attendees as a stored computed field
1160         #. Then add the relevant view
1161
1162         .. patch::
1163
1164 Kanban
1165 ------
1166
1167 Used to organize tasks, production processes, etc… their root element is
1168 ``<kanban>``.
1169
1170 A kanban view shows a set of cards possibly grouped in columns. Each card
1171 represents a record, and each column the values of an aggregation field.
1172
1173 For instance, project tasks may be organized by stage (each column is a
1174 stage), or by responsible (each column is a user), and so on.
1175
1176 Kanban views define the structure of each card as a mix of form elements
1177 (including basic HTML) and :ref:`reference/qweb`.
1178
1179 .. exercise:: Kanban view
1180
1181     Add a Kanban view that displays sessions grouped by course (columns are
1182     thus courses).
1183
1184     .. only:: solutions
1185
1186         #. Add an integer ``color`` field to the *Session* model
1187         #. Add the kanban view and update the action
1188
1189         .. patch::
1190
1191 Workflows
1192 =========
1193
1194 Workflows are models associated to business objects describing their dynamics.
1195 Workflows are also used to track processes that evolve over time.
1196
1197 .. exercise:: Almost a workflow
1198
1199     Add a ``state`` field to the *Session* model. It will be used to define
1200     a workflow-ish.
1201
1202     A sesion can have three possible states: Draft (default), Confirmed and
1203     Done.
1204
1205     In the session form, add a (read-only) field to
1206     visualize the state, and buttons to change it. The valid transitions are:
1207
1208     * Draft ➔ Confirmed
1209     * Confirmed ➔ Draft
1210     * Confirmed ➔ Done
1211     * Done ➔ Draft
1212
1213     .. only:: solutions
1214
1215         #. Add a new ``state`` field
1216         #. Add state-transitioning methods, those can be called from view
1217            buttons to change the record's state
1218         #. And add the relevant buttons to the session's form view
1219
1220         .. patch::
1221
1222 Workflows may be associated with any object in Odoo, and are entirely
1223 customizable. Workflows are used to structure and manage the lifecycles of
1224 business objects and documents, and define transitions, triggers, etc. with
1225 graphical tools. Workflows, activities (nodes or actions) and transitions
1226 (conditions) are declared as XML records, as usual. The tokens that navigate
1227 in workflows are called workitems.
1228
1229 .. exercise:: Workflow
1230
1231     Replace the ad-hoc *Session* workflow by a real workflow. Transform the
1232     *Session* form view so its buttons call the workflow instead of the
1233     model's methods.
1234
1235     .. only:: solutions
1236
1237         .. patch::
1238
1239         .. warning::
1240
1241             A workflow associated with a model is only created when the
1242             model's records are created. Thus there is no workflow instance
1243             associated with session instances created before the workflow's
1244             definition
1245
1246         .. tip::
1247
1248             In order to check if instances of the workflow are correctly
1249             created alongside sessions, go to :menuselection:`Settings -->
1250             Technical --> Workflows --> Instances`
1251
1252
1253
1254 .. exercise:: Automatic transitions
1255
1256     Automatically transition sessions from *Draft* to *Confirmed* when more
1257     than half the session's seats are reserved.
1258
1259     .. only:: solutions
1260
1261         .. patch::
1262
1263 .. exercise:: Server actions
1264
1265     Replace the Python methods for synchronizing session state by
1266     server actions.
1267
1268     Both the workflow and the server actions could have been created entirely
1269     from the UI.
1270
1271     .. only:: solutions
1272
1273         .. patch::
1274
1275 Security
1276 ========
1277
1278 Access control mechanisms must be configured to achieve a coherent security
1279 policy.
1280
1281 Group-based access control mechanisms
1282 -------------------------------------
1283
1284 Groups are created as normal records on the model “res.groups”, and granted
1285 menu access via menu definitions. However even without a menu, objects may
1286 still be accessible indirectly, so actual object-level permissions (read,
1287 write, create, unlink) must be defined for groups. They are usually inserted
1288 via CSV files inside modules. It is also possible to restrict access to
1289 specific fields on a view or object using the field's groups attribute.
1290
1291 Access rights
1292 -------------
1293
1294 Access rights are defined as records of the model “ir.model.access”. Each
1295 access right is associated to a model, a group (or no group for global
1296 access), and a set of permissions: read, write, create, unlink. Such access
1297 rights are usually created by a CSV file named after its model:
1298 ``ir.model.access.csv``.
1299
1300 .. code-block:: text
1301
1302     id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
1303     access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
1304     access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0
1305
1306 .. exercise:: Add access control through the OpenERP interface
1307
1308     Create a new user "John Smith". Then create a group
1309     "OpenAcademy / Session Read" with read access to the *Session* model.
1310
1311     .. only:: solutions
1312
1313         #. Create a new user *John Smith* through
1314            :menuselection:`Settings --> Users --> Users`
1315         #. Create a new group ``session_read`` through
1316            :menuselection:`Settings --> Users --> Groups`, it should have
1317            read access on the *Session* model
1318         #. Edit *John Smith* to make them a member of ``session_read``
1319         #. Log in as *John Smith* to check the access rights are correct
1320
1321 .. exercise:: Add access control through data files in your module
1322
1323     Using data files,
1324
1325     * Create a group *OpenAcademy / Manager* with full access to all
1326       OpenAcademy models
1327     * Make *Session* and *Course* readable by all users
1328
1329     .. only:: solutions
1330
1331         #. Create a new file ``openacademy/security/security.xml`` to
1332            hold the OpenAcademy Manager group
1333         #. Create a new file ``openacademy/security/ir.model.access.csv`` with
1334            the access rights to the models
1335         #. finally update ``openacademy/__openerp__.py`` to add the new data
1336            files to it
1337
1338         .. patch::
1339
1340 Record rules
1341 ------------
1342
1343 A record rule restricts the access rights to a subset of records of the given
1344 model. A rule is a record of the model “ir.rule”, and is associated to a
1345 model, a number of groups (many2many field), permissions to which the
1346 restriction applies, and a domain. The domain specifies to which records the
1347 access rights are limited.
1348
1349 Here is an example of a rule that prevents the deletion of leads that are not
1350 in state “cancel”. Notice that the value of the field “groups” must follow
1351 the same convention as the method “write” of the ORM.
1352
1353 .. code-block:: xml
1354
1355     <record id="delete_cancelled_only" model="ir.rule">
1356         <field name="name">Only cancelled leads may be deleted</field>
1357         <field name="model_id" ref="crm.model_crm_lead"/>
1358         <field name="groups" eval="[(4, ref('base.group_sale_manager'))]"/>
1359         <field name="perm_read" eval="0"/>
1360         <field name="perm_write" eval="0"/>
1361         <field name="perm_create" eval="0"/>
1362         <field name="perm_unlink" eval="1" />
1363         <field name="domain_force">[('state','=','cancel')]</field>
1364     </record>
1365
1366 .. exercise:: Record rule
1367
1368     Add a record rule for the model Course and the group
1369     "OpenAcademy / Manager", that restricts ``write`` and ``unlink`` accesses
1370     to the responsible of a course. If a course has no responsible, all users
1371     of the group must be able to modify it.
1372
1373     .. only:: solutions
1374
1375         Create a new rule in ``openacademy/security/security.xml``:
1376
1377         .. patch::
1378
1379 Internationalization
1380 ====================
1381
1382 Each module can provide its own translations within the i18n directory, by
1383 having files named LANG.po where LANG is the locale code for the language, or
1384 the language and country combination when they differ (e.g. pt.po or
1385 pt_BR.po). Translations will be loaded automatically by Odoo for all
1386 enabled languages. Developers always use English when creating a module, then
1387 export the module terms using Odoo's gettext POT export feature
1388 (Settings>Translations>Export a Translation File without specifying a
1389 language), to create the module template POT file, and then derive the
1390 translated PO files. Many IDE's have plugins or modes for editing and merging
1391 PO/POT files.
1392
1393 .. tip:: The GNU gettext format (Portable Object) used by Odoo is
1394          integrated into LaunchPad, making it an online collaborative
1395          translation platform.
1396
1397 .. code-block:: text
1398
1399    |- idea/ # The module directory
1400       |- i18n/ # Translation files
1401          | - idea.pot # Translation Template (exported from Odoo)
1402          | - fr.po # French translation
1403          | - pt_BR.po # Brazilian Portuguese translation
1404          | (...)
1405
1406 .. tip:: 
1407
1408    By default Odoo's POT export only extracts labels inside XML files or
1409    inside field definitions in Python code, but any Python string can be
1410    translated this way by surrounding it with the tools.translate._ method
1411    (e.g. _(‘Label') )
1412
1413 .. exercise:: Translate a module
1414
1415    Choose a second language for your Odoo installation. Translate your
1416    module using the facilities provided by Odoo.
1417
1418    .. only:: solutions
1419
1420         #. Create a directory ``openacademy/i18n/``
1421         #. Install whichever language you want (
1422            :menuselection:`Administration --> Translations --> Load an
1423            Official Translation`)
1424         #. Synchronize translatable terms (:menuselection`Administration -->
1425            Translations --> Application termsn --> Synchronize Translations`)
1426         #. Create a template translation file by exporting (
1427            :menuselection:`Administration --> Translations -> Import/Export
1428            --> Export Translation`) without specifying a language, save in
1429            ``openacademy/i18n/``
1430         #. Create a translation file by exporting (
1431            :menuselection:`Administration --> Translations --> Import/Export
1432            --> Export Translation`) and specifying a language. Save it in
1433            ``openacademy/i18n/``
1434         #. Open the exported translation file (with a basic text editor or a
1435            dedicated PO-file editor e.g. POEdit_ and translate the missing
1436            terms
1437
1438            .. note::
1439
1440                By default, Odoo's export only extracts labels inside XML
1441                records or Python field definitions, but arbitrary Python
1442                strings can be marked as translatable by calling
1443                :func:`openerp._` with them e.g. ``_("Label")``)
1444
1445         #. Add ``from openerp import _`` to ``course.py`` and
1446            mark missing strings as translatable
1447
1448            .. todo:: there isn't any!
1449
1450         #. Repeat steps 3-6
1451
1452         .. todo:: do we never reload translations?
1453
1454
1455 Reporting
1456 =========
1457
1458 Printed reports
1459 ---------------
1460
1461 Odoo v8 comes with a new report engine based on :ref:`reference/qweb`,
1462 `Twitter Bootstrap`_ and Wkhtmltopdf_. 
1463
1464 A report is a combination two elements:
1465
1466 * an ``ir.actions.report.xml``, for which a ``<report>`` shortcut element is
1467   provided, it sets up various basic parameters for the report (default
1468   type, whether the report should be saved to the database after generation,…)
1469
1470
1471   .. code-block:: xml
1472
1473       <report
1474           id="account_invoices"
1475           model="account.invoice"
1476           string="Invoices"
1477           report_type="qweb-pdf"
1478           name="account.report_invoice"
1479           file="account.report_invoice"
1480           attachment_use="True"
1481           attachment="(object.state in ('open','paid')) and
1482               ('INV'+(object.number or '').replace('/','')+'.pdf')"
1483       />
1484
1485 * A standard :ref:`QWeb view <reference/views/qweb>` for the actual report:
1486
1487   .. code-block:: xml
1488
1489     <t t-call="report.html_container">
1490         <t t-foreach="docs" t-as="o">
1491             <t t-call="report.external_layout">
1492                 <div class="page">
1493                     <h2>Report title</h2>
1494                 </div>
1495             </t>
1496         </t>
1497     </t>
1498
1499     the standard rendering context provides a number of elements, the most
1500     important being:
1501
1502     ``docs``
1503         the records for which the report is printed
1504     ``user``
1505         the user printing the report
1506
1507 Because reports are standard web pages, they are available through a URL and
1508 output parameters can be manipulated through this URL, for instance the HTML
1509 version of the *Invoice* report is available through
1510 http://localhost:8069/report/html/account.report_invoice/1 (if ``account`` is
1511 installed) and the PDF version through
1512 http://localhost:8069/report/pdf/account.report_invoice/1.
1513
1514 .. exercise:: Create a report for the Session model
1515
1516    For each session, it should display session's name, its start and end,
1517    and list the session's attendees.
1518
1519    .. only:: solutions
1520
1521         .. patch::
1522
1523 Dashboards
1524 ----------
1525
1526 .. exercise:: Define a Dashboard
1527
1528    Define a dashboard containing the graph view you created, the sessions
1529    calendar view and a list view of the courses (switchable to a form
1530    view). This dashboard should be available through a menuitem in the menu,
1531    and automatically displayed in the web client when the OpenAcademy main
1532    menu is selected.
1533
1534    .. only:: solutions
1535
1536         #. Create a ``openacademy/views/session_board.xml``. It should contain
1537            the board view, the actions referenced in that view, an action to
1538            open the dashboard and a re-definition of the main menu item to add
1539            the dashboard action
1540
1541            .. note:: Available dashboard styles are ``1``, ``1-1``, ``1-2``,
1542                      ``2-1`` and ``1-1-1``
1543
1544         #. Update ``openacademy/__openerp__.py`` to reference the new data
1545            file
1546
1547         .. patch::
1548
1549 WebServices
1550 ===========
1551
1552 The web-service module offer a common interface for all web-services :
1553
1554 • SOAP
1555 • XML-RPC
1556 • NET-RPC
1557
1558 Business objects can also be accessed via the distributed object
1559 mechanism. They can all be modified via the client interface with contextual
1560 views.
1561
1562 Odoo is accessible through XML-RPC interfaces, for which libraries exist in
1563 many languages.
1564
1565 XML-RPC Library
1566 ---------------
1567
1568 The following example is a Python program that interacts with an Odoo
1569 server with the library xmlrpclib.
1570
1571 ::
1572
1573    import xmlrpclib
1574
1575    root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
1576
1577    uid = xmlrpclib.ServerProxy(root + 'common').login(db, username, password)
1578    print "Logged in as %s (uid: %d)" % (USER, uid)
1579
1580    # Create a new idea
1581    sock = xmlrpclib.ServerProxy(root + 'object')
1582    args = {
1583        'name' : 'Another idea',
1584        'description' : 'This is another idea of mine',
1585        'inventor_id': uid,
1586    }
1587    idea_id = sock.execute(db, uid, password, 'idea.idea', 'create', args)
1588
1589 .. exercise:: Add a new service to the client
1590
1591    Write a Python program able to send XML-RPC requests to a PC running
1592    Odoo (yours, or your instructor's). This program should display all
1593    the sessions, and their corresponding number of seats. It should also
1594    create a new session for one of the courses.
1595
1596    .. only:: solutions
1597
1598         .. code-block:: python
1599
1600             import functools
1601             import xmlrpclib
1602             HOST = 'localhost'
1603             PORT = 8069
1604             DB = 'openacademy'
1605             USER = 'admin'
1606             PASS = 'admin'
1607             ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
1608
1609             # 1. Login
1610             uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
1611             print "Logged in as %s (uid:%d)" % (USER,uid)
1612
1613             call = functools.partial(
1614                 xmlcprlib.ServerProxy(ROOT + 'object').execute,
1615                 DB, uid, PASS)
1616
1617             # 2. Read the sessions
1618             sessions = call('openacademy.session','search_read', [], ['name','seats'])
1619             for session in sessions :
1620                 print "Session %s (%s seats)" % (session['name'], session['seats'])
1621             # 3.create a new session
1622             session_id = call('openacademy.session', 'create', {
1623                 'name' : 'My session',
1624                 'course_id' : 2,
1625             })
1626
1627         Instead of using a hard-coded course id, the code can look up a course
1628         by name::
1629
1630             # 3.create a new session for the "Functional" course
1631             course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
1632             session_id = call('openacademy.session', 'create', {
1633                 'name' : 'My session',
1634                 'course_id' : course_id,
1635             })
1636
1637 .. note:: there are also a number of high-level APIs in various languages to
1638           access Odoo systems without *explicitly* going through XML-RPC e.g.
1639
1640     * https://github.com/akretion/ooor
1641     * https://github.com/syleam/openobject-library
1642     * https://github.com/nicolas-van/openerp-client-lib
1643     * https://pypi.python.org/pypi/oersted/
1644
1645 .. [#autofields] it is possible to :attr:`disable the creation of some
1646                  <openerp.models.Model._log_access>`
1647 .. [#rawsql] writing raw SQL queries is possible, but requires care as it
1648               bypasses all Odoo authentication and security mechanisms.
1649
1650 .. _database index:
1651     http://use-the-index-luke.com/sql/preface
1652
1653 .. _POEdit: http://poedit.net
1654
1655 .. _PostgreSQL's documentation:
1656 .. _table_constraint:
1657     http://www.postgresql.org/docs/9.3/static/ddl-constraints.html
1658
1659 .. _python: http://python.org
1660
1661 .. _XPath: http://w3.org/TR/xpath
1662
1663 .. _twitter bootstrap: http://getbootstrap.com
1664
1665 .. _wkhtmltopdf: http://wkhtmltopdf.org