-
Notifications
You must be signed in to change notification settings - Fork 30
5.0 step4
We now have a few models with data in them. We will now display it to the end user so that he can populate his database with informations.
In step 1, we talked about the module structure, and we created many xml files. There are two families of xml files in tryton:
- Data xml files are those that are referenced in the
tryton.cfg
file, and include record to be included in the database on module installation / upgrade. We have only one such file for now,library.xml
, about which we already talked a little - View xml files are located under the
view
folder in the module and contains view definition and layout. They are referenced from insidelibrary.xml
by thename
attribute of their.ui.view
records
Before writing the contents of our views, we will want to make them accessible
to the user through the tryton client. To do so, we will need to create entry
points via the data xml file library.xml
, so that our users can actually
click somewhere to show up our views.
Open library.xml
, and add the following inside the <data>
tag, right
after it is opened:
<!-- ############# -->
<!-- # Root Menu # -->
<!-- ############# -->
<menuitem name="Library" id="menu_library" sequence="1"/>
This creates a new menuitem
in the database. A menuitem
is an entry
point, an entity that will appear in the main menu on the left side of the
application. The name
parameter is the string that will be used in the
menu, the id
is a unique identifier (like for all xml entries), and
sequence
is an integer used to order the different entry points. 1
will
make it to the top (unless there is another module which adds entry points with
a 0
sequence).
Now if you update your database and connect to it, you should see a new entry in the left-side menu named "Library".
Note: The <menuitem>
accepts an optional icon
attribute which allows
you to set the icon of the menu entity to an image of your liking
In library.xml
, add the following after the view definitions (the
ir.ui.view
records) associated to the library.author
model:
<record model="ir.action.act_window" id="act_author">
<field name="name">Authors</field>
<field name="res_model">library.author</field>
</record>
<record model="ir.action.act_window.view" id="act_author_view_list">
<field name="sequence" eval="10"/>
<field name="view" ref="author_view_list"/>
<field name="act_window" ref="act_author"/>
</record>
<record model="ir.action.act_window.view" id="act_author_view_form">
<field name="sequence" eval="20"/>
<field name="view" ref="author_view_form"/>
<field name="act_window" ref="act_author"/>
</record>
<menuitem parent="menu_library" sequence="1" action="act_author" id="menu_author"/>
We will first focus on the last line. The menuitem
is the same than that of
the "Library" menu we just created. We use the parent
attribute to
reference the main "Library" menu using its id menu_library
. So we are in
essence creating a new menu under the "Library" menu we already created. Here,
the sequence
attribute is used to order the menu among all menus that are
children of the same parent, menu_library
.
You will notice that this menuitem
does not have a name
. That is
because it has a action
attribute. "Actions" in tryton are used to describe
well, actions, that the client is expected to perform when certain conditions
are met. Here, the action will be triggered when the user activates (through
double-click, or selecting then pressing the Enter
key) the menu. When an
action
is set on a menu, the name that will be displayed to the user will
be automatically calculated from the action definition.
Here, the action
value is act_author
. As with all references inside xml
data files, it must match an id
, and indeed we declared a record with this
id
above.
Warning: XML data file are read in the order they are defined. When a
reference to an element is made, the target element must already be created, so
it must be "above". That is why the menuitem
is declared after the action
Let's decompose the action:
<record model="ir.action.act_window" id="act_author">
<field name="name">Authors</field>
<field name="res_model">library.author</field>
</record>
This is a record, the same as when we created views in step 1.
The model is not the same though. It was ir.ui.view
, here it is
ir.action.act_window
. This means that the entity we are creating is an
action, of type "window". Window action will used to create a new tab in the
tryton client for the user to see. The name
attribute will be used as the
tab's title. The res_model
attribute is the __name__
of the model that
will be displayed in the tab.
Note: There are other action types, for now we will only talk about
ir.action.act_window
So here, we are:
- Creating a new entry point which will be "under" the "Library" entry point
- Instructing tryton to, when the user activates the entry point, open a new
tab named
Authors
, which will be used to displaylibrary.author
records
The two remaining entities are records of the ir.action.act_window.view
model. If you remember the naming rules for models that we talked about in
step 1, that sort of indicates that this model is a view
linked to the ir.action.act_window
model.
<record model="ir.action.act_window.view" id="act_author_view_list">
<field name="sequence" eval="10"/>
<field name="view" ref="author_view_list"/>
<field name="act_window" ref="act_author"/>
</record>
We will focus on the first one, the second being very similar. The sequence
attribute is used to order the views that will be used in a tab. Usually, you
will have a list view, and a form view. You almost always want that when the
user opens the tab, he sees the list view, and the form view when he opens a
record. The sequence
attribute allows to do that by ordering the different
views. Why not <field name="sequence">10</field>
? Because then the value
would be a string, and the sequence
attribute expects an integer. So we use
the eval
attribute to tell tryton to python evaluate the string as the
value.
The second attribute is view
. We need to tell tryton which view we want to
use. We already declared in step 1 the views we will need, so we
just need to reference them through their id
using the ref
keyword.
Here, author_view_list
is used to match the id
of the ir.ui.view
record we created before.
The third attribute is used to link the record we are creating to its parent,
the ir.action.act_window
instance with the id act_author
that we just
defined.
All in all, those two ir.action.act_window.view
records are used to explain
to tryton that the tab we want to open should use the two views that we defined
earlier, first the list view, and second the form view.
You can now update your database, restart your server and client, and you
should see a new antry point named Authors
after you expand the Library
entry point. Double-clicking it should open a new ampty tab named Authors
.
Nothing is displayed here, because we did not (yet) add any contents to our
views, we are coming to that now.
We are now going to flesh up our views, so that we can actually start to use our library application.
Open the view/author_list.xml
file, and write the following inside the
<tree>
tag:
<field name="name"/>
That's it. Tree views are the simplest, because the only thing we define in
them is the list of columns we want to display. We usually only define
<field/>
tags in them, with a sole attribute, name
, whose value is the
name of the field we want to display.
There are some options you can use. Rewrite the previous line with the following:
<field name="name" expand="1"/>
The expand
attribute in tree views tells the client to expand the column
size as much as possible. Since we only have one column, we should do so. If
many columns have the expand
attribute set:
- The columns without it will be shrinked as much as possible (depending on the contents size so as to still be readable)
- The remaining space will be equally divided for the
expand
ed columns
Tree views can usually be filtered on their columns. You cannot filter on a
field that is not displayed in the view. If you want to make it possible to
do so anyway, you can add the field in the view with the tree_invisible
attribute set:
<field name="my_invisible_field" tree_invisible="1"/>
Doing so will make the client load the field, and allow filtering from the client side.
It is possible to sort a list by clicking on the columns headers.
Edit view/author_form.xml
and add the following inside the <form/>
tag:
<label name="name"/>
<field name="name"/>
<label name="gender"/>
<field name="gender"/>
The <field>
tag is similar to that of the tree view. The <label>
tag is
used to display the field's string as a text to the user. Remember, in all
field definition, there is a "nice text" whose purpose is to describe the field
to the end user. That's where it is used.
The <label>
and <field>
are the building blocks of all form views. Form
views without <field>
tags are totally static, because they will not fetch
any data linked to the record a user is consulting.
Right now, you can restart your server. The Authors
entry point will now,
when opened, display a list view with a column named Name
. You can create a
new author by Hitting "Ctrl+n" or clicking the "New" button in the toolbar.
This will switch you to the form view, in which you will be able to set the
name
and gender
fields, save (note how the client blocks you from
saving if you do not set the name
field), then switch back to the list view
(by hitting "Ctrl+l" or clicking the "Switch" button) and see your newly
created author.
Reopen view/author_form.xml
and add the following after the gender
field:
<separator id="dates" string="Dates" colspan="4"/>
<label name="birth_date"/>
<field name="birth_date"/>
<label name="death_date"/>
<field name="death_date"/>
<field name="books" colspan="4"/>
The <label>
and <field>
tags should be clear enough by now. Let's focus
on the separator
tag.
A separator is used to separate (...) elements in the view. It is displayed as
an horizontal line, with (eventually) a string in it. Here we set a string
("Dates") and an id
since it is not linked to a field. The complicated part
is the colspan
attributes.
Tryton forms are basically a grid. By default (it can be changed), this grid has four columns, which are filled up from left to right, top to bottom. When you write:
<label name="birth_date"/>
<field name="birth_date"/>
you fill up one cell of the grid with the label, and the next one with the field. If the label is the four-th element of a row, the field will be the first element of the next row.
Column sizes cannot be controlled, they are automatically calculated bu the client so that the bigger cell in the column fits. So if the same column has a very big label, for instance if you defined a field like the following:
my_field = fields.Char('This is a very long field especially designed for problems')
the column in which the <label name="my_field"/>
will be made long enough
to acommodate this length, whcih may no be very aesthetic.
Back to our separator
. The colspan
attribute is used to control how
many columns our element should take in the row. Here we are setting
colspan
to 4
, so four columns. As said earlier, the default views have
four columns, so we want it to take the full width of the view.
Note: If we require four columns but our element starts at column 2, it will
be moved at the beginning of the next line. Using colspan
does not risk for
the client to somehow "split" your element across two rows
You notice that we use colspan
as well for the books
field. This is
because the default widget for One2Many
fields is bigger than the standard
Char
or Selection
widgets, and multiline, so for readability it is
common to set its width to the full screen, or half of that.
Restart the server / client, then check out the new form and see how the xml file contents reflects in the view layout.
Open the view/book_form.xml
file, and complete it inside the <form>
tag:
<group id="left" string="" colspan="1" xexpand="0" xfill="1">
<field name="cover" widget="image"/>
</group>
<group id="right" string="" colspan="3" col="2" yfill="1">
<label name="title"/>
<field name="title"/>
<label name="author"/>
<field name="author"/>
<label name="genre"/>
<field name="genre"/>
<label name="editor"/>
<field name="editor"/>
</group>
<notebook colspan="4">
<page name="summary" col="2">
<label name="description"/>
<field name="description"/>
<separator name="summary" colspan="2"/>
<field name="summary" colspan="2"/>
</page>
<page id="other_data" string="Other informations">
<label name="page_count"/>
<field name="page_count"/>
<label name="edition_stopped"/>
<field name="edition_stopped"/>
</page>
</notebook>
Note: You will not be able to consult this view until you do your homework. Do not hesitate to come back here once it is done to see how its contents are displayed
This view uses other tags and parameters, we will detail them now:
- The
<group>
tag is used to create a sub-grid in the main grid. It must be identified by either anid
andstring
to match, or aname
(in which case the string will be that of the field whose name in thename
tag) - The
xexpand
andxfill
attributes are often used together to control how the group (or field, those attributes are available on multiple elements) tries to use the available space in the view. The same asexpand
in the tree view, elements with thexexpand
attribute set will try to take the maximum available space in terms of column size. Thexfill
attribute will usually be set to "1", it is used to tell the contents of thegroup
to take as much space as possible inside the group - There are
yexpand
andyfill
attributes as well, which work similarly on the "y" axis - The
widget
attribute is used to force a field to be displayed using a widget. Every field type has a default widget that is usually good as is, however you can force it to a compatible widget. Here thecover
field is aBinary
field. The default widget allows to upload a file, retrieve it, or open it. We assume that the cover files will be picture, so we force theimage
widget so that they are directly shown to the user - The
right
group has a newcol
attribute. Remember that group are basically "sub-grids" of the main 4-column grid you are designing your form in. This attribute controls how many columns there will be in the sub-grid. By the way, modifying the number of columns in the main grid is done by setting thecol
attribute on theform
tag
Here the combination of the left
and right
groups is used to have a
small group on the top-left part of the view which will contain the cover
field, and a big group on the right with more data. The relative horizontal
size of the groups is controlled by the combination of the xexpand
and
colspan
attributes on both groups. The yfill
attribute is used so that
the right group contents are not vertically centered but rather at the top.
notebook
elements, used in conjunction with the page
element, arre used
to describe sets of tabs to better organize your data. Here we want a "main"
page to contain a short description of the book and a more complete summary,
and another one for less frequently used informations.
Tryton's views may take a while to get accustomed to, and feel a little limited at first, but with a little practice it becomes possible to rather quickly design and test views which will "do the job". You may not have pixel control, but it should suit most of your needs anyway.
What may be bothersome is writing all this xml, if you do not already do so, you should consider investigating snippets for your text editor, which will make your life all the easier when creating / editing views.
Note: We only covered the basics of views, there are various options / widgets that tryton supports which may suit some more specific needs. Please read the documentation for more information about those
Be careful when choosing the id
of your xml entries. There is usually a
"standard" pattern for each model:
-
<model>_view_<view_type>
for views -
act_<model>
for actions -
menu_<model>
for entry points etc.
Those are not hard rules, more guidelines that you should follow unless there is a reason that forbid you to (duplicate, readability, etc.)
Regarding the development process, here are the rules:
- When modifying a XML data file (in our case,
library.xml
), you MUST update your database and restart the server in order for your modifications to be visible - When modifying a view, just restarting the server, and reopening the view should be enough. Reopening means closing all tabs where the view may be displayed, and opening them again. If it does not work, you can try restarting the server
Something you will have to worry about when writing views is how your records
will appear when they are presented as relation. A Char
field contents will
obviously be its value, but what about a Many2One
field ?
Tryton has an internal hidden field named rec_name
which stands for "record
name", and is used to display the record to the user. By default, the
rec_name
of a model will be:
- If the field has field named
name
, this field's value will be used. So no worries forlibrary.editor
,library.genre
andlibrary.author
, which will be displayed using their names - Else, the
id
field is used, so the displayed value to the user will be an integer
Fortunately, tryton allows to modify this. The easiest way to do so is by
setting the _rec_name
variable on a model. Add this line right under the
__name__ = 'library.book'
line:
_rec_name = 'title'
By doing so, you instruct tryton to use the contents of the title
field as
the rec_name
. So from now on, every time a library.book
will be
displayed as a Many2One
to the end user, it will be as the book's title.
... you read a little too quickly the "Add an Author entry point" section, you can now finally visualize what you did until now by updating / restarting the server, and restarting the client!
- Add the missing
_rec_name
inlibrary.py
- Add a "Configuration" entry point under the existing "Library" entry point
- Create the "Editor" and "Genre" entry points under this entry point
- Add a "Book" entry point below "Authors"
- Fill up all the views. There is no "better" way, the correction will only be a reasonable version. You may also improve the views we wrote above
- Try to modify the views with various attributes, to get a feel of how things work. Design the view on paper, then try to make it work. Ask around if you need help on how you can achieve what you want
You can compare your code with that of the step4_homework
branch for
differences. You can then read here about what you
should have done, and why.
We are starting to have a usable library management module. The next step will be dedicated to function fields, which are critical to make a good tryton application.