[FIX] doc: typos and code samples from howtos
[odoo/odoo.git] / doc / howtos / backend.rst
index 7af76db..e1ba28e 100644 (file)
@@ -1,14 +1,14 @@
 .. queue:: backend/series
 
-=======
-Backend
-=======
+=================
+Building a Module
+=================
 
 Start/Stop the Odoo server
 ==========================
 
 Odoo uses a client/server architecture in which clients are web browsers
-accessing the odoo server via RPC.
+accessing the Odoo server via RPC.
 
 Business logic and extension is generally performed on the server side,
 although supporting client features (e.g. new data representation such as
@@ -181,7 +181,7 @@ Some attributes are available on all fields, here are the most common ones:
     If ``True``, the field can not be empty, it must either have a default
     value or always be given a value when creating a record.
 :attr:`~openerp.fields.Field.help` (``unicode``, default: ``''``)
-    Long-formm, provides a help tooltip to users in the UI.
+    Long-form, provides a help tooltip to users in the UI.
 :attr:`~openerp.fields.Field.index` (``bool``, default: ``False``)
     Requests that Odoo create a `database index`_ on the column
 
@@ -615,7 +615,7 @@ The second inheritance mechanism (delegation) allows to link every record of a
 model to a record in a parent model, and provides transparent access to the
 fields of the parent record.
 
-.. image:: backend/inheritance_methods.png
+.. image:: ../images/inheritance_methods.png
     :align: center
 
 .. seealso::
@@ -839,6 +839,19 @@ float, string), or a function taking a recordset and returning a value::
     name = fields.Char(default="Unknown")
     user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
 
+.. note::
+
+    The object ``self.env`` gives access to request parameters and other useful
+    things:
+
+    - ``self.env.cr`` or ``self._cr`` is the database *cursor* object; it is
+      used for querying the database
+    - ``self.env.uid`` or ``self._uid`` is the current user's database id
+    - ``self.env.user`` is the current user's record
+    - ``self.env.context`` or ``self._context`` is the context dictionary
+    - ``self.env.ref(xml_id)`` returns the record corresponding to an XML id
+    - ``self.env[model_name]`` returns an instance of the given model
+
 .. exercise:: Active objects – Default values
 
     * Define the start_date default value as today (see
@@ -909,7 +922,7 @@ Model constraints
 
 Odoo provides two ways to set up automatically verified invariants:
 :func:`Python constraints <openerp.api.constrains>` and
-:attr:`SQL constaints <openerp.models.Model._sql_constraints>`.
+:attr:`SQL constraints <openerp.models.Model._sql_constraints>`.
 
 A Python constraint is defined as a method decorated with
 :func:`~openerp.api.constrains`, and invoked on a recordset. The decorator
@@ -1051,21 +1064,35 @@ field (to define the label for each calendar event)
 Search views
 ------------
 
-Search view fields can take custom operators or :ref:`reference/orm/domains`
-for more flexible matching of results.
+Search view ``<field>`` elements can have a ``@filter_domain`` that overrides
+the domain generated for searching on the given field. In the given domain,
+``self`` represents the value entered by the user. In the example below, it is
+used to search on both fields ``name`` and ``description``.
 
-Search views can also contain *filters* which act as toggles for predefined
-searches (defined using :ref:`reference/orm/domains`):
+Search views can also contain ``<filter>`` elements, which act as toggles for
+predefined searches. Filters must have one of the following attributes:
+
+``domain``
+    add the given domain to the current search
+``context``
+    add some context to the current search; use the key ``group_by`` to group
+    results on the given field name
 
 .. code-block:: xml
 
     <search string="Ideas">
-        <filter name="my_ideas" domain="[('inventor_id','=',uid)]"
-                string="My Ideas" icon="terp-partner"/>
         <field name="name"/>
-        <field name="description"/>
+        <field name="description" string="Name and description"
+               filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
         <field name="inventor_id"/>
         <field name="country_id" widget="selection"/>
+
+        <filter name="my_ideas" string="My Ideas"
+                domain="[('inventor_id', '=', uid)]"/>
+        <group string="Group By">
+            <filter name="group_by_inventor" string="Inventor"
+                    context="{'group_by': 'inventor'}"/>
+        </group>
     </search>
 
 To use a non-default search view in an action, it should be linked using the
@@ -1079,8 +1106,9 @@ default and behave as booleans (they can only be enabled by default).
 
 .. exercise:: Search views
 
-    Add a button to filter the courses for which the current user is the
-    responsible in the course search view. Make it selected by default.
+    #. Add a button to filter the courses for which the current user is the
+       responsible in the course search view. Make it selected by default.
+    #. Add a button to group courses by responsible user.
 
     .. only:: solutions
 
@@ -1213,10 +1241,10 @@ Workflows are also used to track processes that evolve over time.
     In the session form, add a (read-only) field to
     visualize the state, and buttons to change it. The valid transitions are:
 
-    * Draft ➔ Confirmed
-    * Confirmed ➔ Draft
-    * Confirmed ➔ Done
-    * Done ➔ Draft
+    * Draft -> Confirmed
+    * Confirmed -> Draft
+    * Confirmed -> Done
+    * Done -> Draft
 
     .. only:: solutions
 
@@ -1234,6 +1262,13 @@ graphical tools. Workflows, activities (nodes or actions) and transitions
 (conditions) are declared as XML records, as usual. The tokens that navigate
 in workflows are called workitems.
 
+.. warning::
+
+    A workflow associated with a model is only created when the
+    model's records are created. Thus there is no workflow instance
+    associated with session instances created before the workflow's
+    definition
+
 .. exercise:: Workflow
 
     Replace the ad-hoc *Session* workflow by a real workflow. Transform the
@@ -1244,13 +1279,6 @@ in workflows are called workitems.
 
         .. patch::
 
-        .. warning::
-
-            A workflow associated with a model is only created when the
-            model's records are created. Thus there is no workflow instance
-            associated with session instances created before the workflow's
-            definition
-
         .. tip::
 
             In order to check if instances of the workflow are correctly
@@ -1289,7 +1317,7 @@ policy.
 Group-based access control mechanisms
 -------------------------------------
 
-Groups are created as normal records on the model “res.groups”, and granted
+Groups are created as normal records on the model ``res.groups``, and granted
 menu access via menu definitions. However even without a menu, objects may
 still be accessible indirectly, so actual object-level permissions (read,
 write, create, unlink) must be defined for groups. They are usually inserted
@@ -1299,7 +1327,7 @@ specific fields on a view or object using the field's groups attribute.
 Access rights
 -------------
 
-Access rights are defined as records of the model “ir.model.access”. Each
+Access rights are defined as records of the model ``ir.model.access``. Each
 access right is associated to a model, a group (or no group for global
 access), and a set of permissions: read, write, create, unlink. Such access
 rights are usually created by a CSV file named after its model:
@@ -1349,14 +1377,14 @@ Record rules
 ------------
 
 A record rule restricts the access rights to a subset of records of the given
-model. A rule is a record of the model “ir.rule”, and is associated to a
+model. A rule is a record of the model ``ir.rule``, and is associated to a
 model, a number of groups (many2many field), permissions to which the
 restriction applies, and a domain. The domain specifies to which records the
 access rights are limited.
 
 Here is an example of a rule that prevents the deletion of leads that are not
-in state “cancel”. Notice that the value of the field “groups” must follow
-the same convention as the method “write” of the ORM.
+in state ``cancel``. Notice that the value of the field ``groups`` must follow
+the same convention as the method :meth:`~openerp.models.Model.write` of the ORM.
 
 .. code-block:: xml
 
@@ -1384,6 +1412,94 @@ the same convention as the method “write” of the ORM.
 
         .. patch::
 
+Wizards
+=======
+
+Wizards describe interactive sessions with the user (or dialog boxes) through
+dynamic forms. A wizard is simply a model that extends the class
+:class:`~openerp.models.TransientModel` instead of
+:class:`~openerp.models.Model`. The class
+:class:`~openerp.models.TransientModel` extends :class:`~openerp.models.Model`
+and reuse all its existing mechanisms, with the following particularities:
+
+- Wizard records are not meant to be persistent; they are automatically deleted
+  from the database after a certain time. This is why they are called
+  *transient*.
+- Wizard models do not require explicit access rights: users have all
+  permissions on wizard records.
+- Wizard records may refer to regular records or wizard records through many2one
+  fields, but regular records *cannot* refer to wizard records through a
+  many2one field.
+
+We want to create a wizard that allow users to create attendees for a particular
+session, or for a list of sessions at once.
+
+.. exercise:: Define the wizard
+
+    Create a wizard model with a many2one relationship with the *Session*
+    model and a many2many relationship with the *Partner* model.
+
+    .. only:: solutions
+
+        Add a new file ``openacademy/wizard.py``:
+
+        .. patch::
+
+Launching wizards
+-----------------
+
+Wizards are launched by ``ir.actions.act_window`` records, with the field
+``target`` set to the value ``new``. The latter opens the wizard view into a
+popup window. The action may be triggered by a menu item.
+
+There is another way to launch the wizard: using an ``ir.actions.act_window``
+record like above, but with an extra field ``src_model`` that specifies in the
+context of which model the action is available. The wizard will appear in the
+contextual actions of the model, above the main view. Because of some internal
+hooks in the ORM, such an action is declared in XML with the tag ``act_window``.
+
+.. code:: xml
+
+    <act_window id="launch_the_wizard"
+                name="Launch the Wizard"
+                src_model="context_model_name"
+                res_model="wizard_model_name"
+                view_mode="form"
+                target="new"
+                key2="client_action_multi"/>
+
+Wizards use regular views and their buttons may use the attribute
+``special="cancel"`` to close the wizard window without saving.
+
+.. exercise:: Launch the wizard
+
+    #. Define a form view for the wizard.
+    #. Add the action to launch it in the context of the *Session* model.
+    #. Define a default value for the session field in the wizard; use the
+       context parameter ``self._context`` to retrieve the current session.
+
+    .. only:: solutions
+
+        .. patch::
+
+.. exercise:: Register attendees
+
+    Add buttons to the wizard, and implement the corresponding method for adding
+    the attendees to the given session.
+
+    .. only:: solutions
+
+        .. patch::
+
+.. exercise:: Register attendees to multiple sessions
+
+    Modify the wizard model so that attendees can be registered to multiple
+    sessions.
+
+    .. only:: solutions
+
+        .. patch::
+
 Internationalization
 ====================
 
@@ -1443,8 +1559,8 @@ for editing and merging PO/POT files.
            dedicated PO-file editor e.g. POEdit_ and translate the missing
            terms
 
-        #. Add ``from openerp import _`` to ``course.py`` and
-           mark missing strings as translatable
+        #. In ``models.py``, add an import statement for the function
+           ``openerp._`` and mark missing strings as translatable
 
         #. Repeat steps 3-6
 
@@ -1566,25 +1682,23 @@ XML-RPC Library
 ---------------
 
 The following example is a Python program that interacts with an Odoo
-server with the library xmlrpclib.
-
-::
+server with the library ``xmlrpclib``::
 
    import xmlrpclib
 
    root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
 
-   uid = xmlrpclib.ServerProxy(root + 'common').login(db, username, password)
+   uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)
    print "Logged in as %s (uid: %d)" % (USER, uid)
 
-   # Create a new idea
+   # Create a new note
    sock = xmlrpclib.ServerProxy(root + 'object')
    args = {
-       'name' : 'Another idea',
-       'description' : 'This is another idea of mine',
-       'inventor_id': uid,
+       'color' : 8,
+       'memo' : 'This is a note',
+       'create_uid': uid,
    }
-   idea_id = sock.execute(db, uid, password, 'idea.idea', 'create', args)
+   note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)
 
 .. exercise:: Add a new service to the client
 
@@ -1611,12 +1725,12 @@ server with the library xmlrpclib.
             print "Logged in as %s (uid:%d)" % (USER,uid)
 
             call = functools.partial(
-                xmlcprlib.ServerProxy(ROOT + 'object').execute,
+                xmlrpclib.ServerProxy(ROOT + 'object').execute,
                 DB, uid, PASS)
 
             # 2. Read the sessions
             sessions = call('openacademy.session','search_read', [], ['name','seats'])
-            for session in sessions :
+            for session in sessions:
                 print "Session %s (%s seats)" % (session['name'], session['seats'])
             # 3.create a new session
             session_id = call('openacademy.session', 'create', {
@@ -1634,8 +1748,77 @@ server with the library xmlrpclib.
                 'course_id' : course_id,
             })
 
-.. note:: there are also a number of high-level APIs in various languages to
-          access Odoo systems without *explicitly* going through XML-RPC e.g.
+JSON-RPC Library
+----------------
+
+The following example is a Python program that interacts with an Odoo server
+with the standard Python libraries ``urllib2`` and ``json``::
+
+    import json
+    import random
+    import urllib2
+
+    def json_rpc(url, method, params):
+        data = {
+            "jsonrpc": "2.0",
+            "method": method,
+            "params": params,
+            "id": random.randint(0, 1000000000),
+        }
+        req = urllib2.Request(url=url, data=json.dumps(data), headers={
+            "Content-Type":"application/json",
+        })
+        reply = json.load(urllib2.urlopen(req))
+        if reply.get("error"):
+            raise Exception(reply["error"])
+        return reply["result"]
+
+    def call(url, service, method, *args):
+        return json_rpc(url, "call", {"service": service, "method": method, "args": args})
+
+    # log in the given database
+    url = "http://%s:%s/jsonrpc" % (HOST, PORT)
+    uid = call(url, "common", "login", DB, USER, PASS)
+
+    # create a new note
+    args = {
+        'color' : 8,
+        'memo' : 'This is another note',
+        'create_uid': uid,
+    }
+    note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)
+
+Here is the same program, using the library
+`jsonrpclib <https://pypi.python.org/pypi/jsonrpclib>`::
+
+    import jsonrpclib
+
+    # server proxy object
+    url = "http://%s:%s/jsonrpc" % (HOST, PORT)
+    server = jsonrpclib.Server(url)
+
+    # log in the given database
+    uid = server.call(service="common", method="login", args=[DB, USER, PASS])
+
+    # helper function for invoking model methods
+    def invoke(model, method, *args):
+        args = [DB, uid, PASS, model, method] + list(args)
+        return server.call(service="object", method="execute", args=args)
+
+    # create a new note
+    args = {
+        'color' : 8,
+        'memo' : 'This is another note',
+        'create_uid': uid,
+    }
+    note_id = invoke('note.note', 'create', args)
+
+Examples can be easily adapted from XML-RPC to JSON-RPC.
+
+.. note::
+
+    There are a number of high-level APIs in various languages to access Odoo
+    systems without *explicitly* going through XML-RPC or JSON-RPC, such as:
 
     * https://github.com/akretion/ooor
     * https://github.com/syleam/openobject-library