[IMP] doc/howtos/backend: add section on wizards

This commit is contained in:
Raphael Collet 2014-08-27 16:06:30 +02:00
parent 64f65ee8f6
commit f7aa1ae764
10 changed files with 264 additions and 114 deletions

View File

@ -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
@ -1289,7 +1302,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 +1312,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 +1362,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 ``write`` of the ORM.
.. code-block:: xml
@ -1384,6 +1397,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
====================

View File

@ -3,11 +3,11 @@
Index: addons/openacademy/models.py
===================================================================
--- addons.orig/openacademy/models.py 2014-08-26 17:26:07.479783261 +0200
+++ addons/openacademy/models.py 2014-08-26 17:26:07.475783261 +0200
@@ -14,6 +14,16 @@
'openacademy.session', 'course_id', string="Session")
--- addons.orig/openacademy/models.py 2014-08-27 14:19:33.099111181 +0200
+++ addons/openacademy/models.py 2014-08-27 14:20:03.311110732 +0200
@@ -13,6 +13,16 @@
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")
+ _sql_constraints = [
+ ('name_description_check',
@ -19,6 +19,6 @@ Index: addons/openacademy/models.py
+ "The course title must be unique"),
+ ]
+
class Session(models.Model):
_name = 'openacademy.session'

View File

@ -3,11 +3,11 @@
Index: addons/openacademy/models.py
===================================================================
--- addons.orig/openacademy/models.py 2014-08-26 17:26:08.359783248 +0200
+++ addons/openacademy/models.py 2014-08-26 17:26:08.351783248 +0200
@@ -14,6 +14,20 @@
'openacademy.session', 'course_id', string="Session")
--- addons.orig/openacademy/models.py 2014-08-27 14:20:12.363110598 +0200
+++ addons/openacademy/models.py 2014-08-27 14:20:12.359110598 +0200
@@ -13,6 +13,20 @@
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")
+ @api.one
+ def copy(self, default=None):

View File

@ -1,93 +0,0 @@
# HG changeset patch
# Parent 1299668a15a9359d4ef77d8f5231816c7de476fa
Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-26 17:26:00.403783366 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-26 17:26:00.399783366 +0200
@@ -9,13 +9,19 @@
<sheet>
<group>
<field name="name"/>
+ <field name="responsible_id"/>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
- <page string="About">
- This is an example of notebooks
+ <page string="Sessions">
+ <field name="session_ids">
+ <tree string="Registered sessions">
+ <field name="name"/>
+ <field name="instructor_id"/>
+ </tree>
+ </field>
</page>
</notebook>
</sheet>
@@ -34,6 +40,18 @@
</field>
</record>
+ <!-- override the automatically generated list view for courses -->
+ <record model="ir.ui.view" id="course_tree_view">
+ <field name="name">course.tree</field>
+ <field name="model">openacademy.course</field>
+ <field name="arch" type="xml">
+ <tree string="Course Tree">
+ <field name="name"/>
+ <field name="responsible_id"/>
+ </tree>
+ </field>
+ </record>
+
<!-- window action -->
<!--
The following tag is an action definition for a "window action",
@@ -65,6 +83,44 @@
action="openacademy.course_list_action"
It is not required when it is the same module -->
+ <!-- session's form view -->
+ <record model="ir.ui.view" id="session_form_view">
+ <field name="name">session.form</field>
+ <field name="model">openacademy.session</field>
+ <field name="arch" type="xml">
+ <form string="Session Form">
+ <sheet>
+ <group>
+ <group string="General">
+ <field name="course_id"/>
+ <field name="name"/>
+ <field name="instructor_id"/>
+ </group>
+ <group string="Schedule">
+ <field name="start_date"/>
+ <field name="duration"/>
+ <field name="seats"/>
+ </group>
+ </group>
+ <label for="attendee_ids"/>
+ <field name="attendee_ids"/>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <!-- session's tree/list view -->
+ <record model="ir.ui.view" id="session_tree_view">
+ <field name="name">session.tree</field>
+ <field name="model">openacademy.session</field>
+ <field name="arch" type="xml">
+ <tree string="Session Tree">
+ <field name="name"/>
+ <field name="course_id"/>
+ </tree>
+ </field>
+ </record>
+
<record model="ir.actions.act_window" id="session_list_action">
<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>

View File

@ -3,21 +3,21 @@
Index: addons/openacademy/models.py
===================================================================
--- addons.orig/openacademy/models.py 2014-08-27 10:37:24.591932036 +0200
+++ addons/openacademy/models.py 2014-08-27 10:37:24.583932036 +0200
--- addons.orig/openacademy/models.py 2014-08-27 14:18:59.091111685 +0200
+++ addons/openacademy/models.py 2014-08-27 14:19:03.259111624 +0200
@@ -10,6 +10,8 @@
responsible_id = fields.Many2one('res.users',
ondelete='set null', string="Responsible", index=True)
+ session_ids = fields.One2many(
+ 'openacademy.session', 'course_id', string="Session")
+ 'openacademy.session', 'course_id', string="Sessions")
class Session(models.Model):
Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-27 10:37:24.591932036 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-27 10:37:24.583932036 +0200
--- addons.orig/openacademy/views/openacademy.xml 2014-08-27 14:18:59.091111685 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-27 14:18:59.083111686 +0200
@@ -15,8 +15,13 @@
<page string="Description">
<field name="description"/>

View File

@ -0,0 +1,24 @@
Index: addons/openacademy/__init__.py
===================================================================
--- addons.orig/openacademy/__init__.py 2014-08-27 14:19:23.023111330 +0200
+++ addons/openacademy/__init__.py 2014-08-27 14:22:25.043108628 +0200
@@ -2,3 +2,4 @@
import controllers
import models
import partner
+import wizard
Index: addons/openacademy/wizard.py
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ addons/openacademy/wizard.py 2014-08-27 14:24:39.195106637 +0200
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+from openerp import models, fields, api
+
+class Wizard(models.TransientModel):
+ _name = 'openacademy.wizard'
+
+ session_id = fields.Many2one('openacademy.session',
+ string="Session", required=True)
+ attendee_ids = fields.Many2many('res.partner', string="Attendees")

View File

@ -0,0 +1,30 @@
Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-27 15:01:00.355074258 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-27 15:04:45.891070910 +0200
@@ -245,6 +245,12 @@
<field name="session_id"/>
<field name="attendee_ids"/>
</group>
+ <footer>
+ <button name="subscribe" type="object"
+ string="Subscribe" class="oe_highlight"/>
+ or
+ <button special="cancel" string="Cancel"/>
+ </footer>
</form>
</field>
</record>
Index: addons/openacademy/wizard.py
===================================================================
--- addons.orig/openacademy/wizard.py 2014-08-27 14:43:02.323090261 +0200
+++ addons/openacademy/wizard.py 2014-08-27 15:05:28.407070278 +0200
@@ -11,3 +11,8 @@
session_id = fields.Many2one('openacademy.session',
string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
+
+ @api.multi
+ def subscribe(self):
+ self.session_id.attendee_ids |= self.attendee_ids
+ return {}

View File

@ -0,0 +1,46 @@
Index: addons/openacademy/wizard.py
===================================================================
--- addons.orig/openacademy/wizard.py 2014-08-27 14:24:39.195106637 +0200
+++ addons/openacademy/wizard.py 2014-08-27 14:43:02.323090261 +0200
@@ -5,6 +5,9 @@
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'
+ def _default_session(self):
+ return self.env['openacademy.session'].browse(self._context.get('active_id'))
+
session_id = fields.Many2one('openacademy.session',
- string="Session", required=True)
+ string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-27 14:20:50.071110038 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-27 15:01:00.355074258 +0200
@@ -235,5 +235,26 @@
<menuitem id="session_menu" name="Sessions"
parent="openacademy_menu"
action="session_list_action"/>
+
+ <record model="ir.ui.view" id="wizard_form_view">
+ <field name="name">wizard.form</field>
+ <field name="model">openacademy.wizard</field>
+ <field name="arch" type="xml">
+ <form string="Add Attendees">
+ <group>
+ <field name="session_id"/>
+ <field name="attendee_ids"/>
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <act_window id="launch_session_wizard"
+ name="Add Attendees"
+ src_model="openacademy.session"
+ res_model="openacademy.wizard"
+ view_mode="form"
+ target="new"
+ key2="client_action_multi"/>
</data>
</openerp>

View File

@ -0,0 +1,38 @@
Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-27 15:04:45.891070910 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-27 15:27:13.919050898 +0200
@@ -242,7 +242,7 @@
<field name="arch" type="xml">
<form string="Add Attendees">
<group>
- <field name="session_id"/>
+ <field name="session_ids"/>
<field name="attendee_ids"/>
</group>
<footer>
Index: addons/openacademy/wizard.py
===================================================================
--- addons.orig/openacademy/wizard.py 2014-08-27 15:05:28.407070278 +0200
+++ addons/openacademy/wizard.py 2014-08-27 15:27:07.119050999 +0200
@@ -5,14 +5,15 @@
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'
- def _default_session(self):
- return self.env['openacademy.session'].browse(self._context.get('active_id'))
+ def _default_sessions(self):
+ return self.env['openacademy.session'].browse(self._context.get('active_ids'))
- session_id = fields.Many2one('openacademy.session',
- string="Session", required=True, default=_default_session)
+ session_ids = fields.Many2many('openacademy.session',
+ string="Sessions", required=True, default=_default_sessions)
attendee_ids = fields.Many2many('res.partner', string="Attendees")
@api.multi
def subscribe(self):
- self.session_id.attendee_ids |= self.attendee_ids
+ for session in self.session_ids:
+ session.attendee_ids |= self.attendee_ids
return {}

View File

@ -30,6 +30,10 @@ exercise-state-workflow-automatic
exercise-state-workflow-actions
exercise-access-rights
exercise-access-rules
exercise-wizard
exercise-wizard-launch
exercise-wizard-action
exercise-wizard-multi
exercise-translations
exercise-report
exercise-dashboard