-
Notifications
You must be signed in to change notification settings - Fork 30
4.4 step1
For this first step, you will need to checkout the associated git branch in the training module:
git checkout 4.4/step1
This step will focus on explaining the file structure of a module, and its basic components.
When checking out the branch, you will find the library
module under the
modules
directory of your local copy of the training repository. For now
all files are empty, but that shall change shortly.
Here is the structure of a basic tryton module :
library
├─ tryton.cfg
├─ __init__.py
├─ library.py
├─ library.xml
└─ view
├─ author_form.xml
└─ author_list.xml
We are going to detail each and every one of those files, and start to build up our module.
Open the tryton.cfg
file and write down the following:
[tryton]
depends:
ir
res
xml:
library.xml
This file will be used by tryton to understand how it should install / upgrade your module.
The depends
section contains the modules on which your module
depends. Here we only specify the ir
and res
modules. Those modules are
the basic tryton modules, and are directly built into the server.
When your module extends models from other modules, or requires in any way that another module be installed for your it to behave as intended, you should add it there. There is no requirement on the module order, though for readability purposes it is recommanded to order them depending on their level in the module hierarchy (i.e. more general to more specific).
The ir
module stands for Internal Resources. It contains all the models
that are needed to manage the client interface. That includes menus, views,
actions, etc...
The source code of this module is available here for further reading.
The res
module stands for Resources. It contains other resources that are
needed for the trytond server to behave as expected. Typically, you can find in
there all the models that are used for user / access-right management.
The source code of this module is available here for further reading.
The xml
section is the list of xml files that should be loaded by the
server when installing or upgrading your module. Here there is only
library.xml
, which we will describe later on. The order is important here,
because the contents of a file may depend on that of another. So if the order
is wrong, installing or updating your server may fail because the server will
look for an entity it does not yet installed.
Those xml files are used to create the necessary informations in the database for your module. This will usually include UI related elements (views, entry points, etc...), authorizations defaults, and configuration elements. This will be further detailed later on.
Open the __init__.py
file and write down the following:
from trytond.pool import Pool
import library
def register():
Pool.register(
library.Author,
module='library', type_='model')
The __init__.py
file is where all standard python modules describe their
contents and make them available. In the case of a tryton module, it has a very
specific purpose: teach the tryton server what your module does, and how to do
it. This is done by registering the python classes that describe the
constitutive elements of your module. We use the word register because the
server will aggregate the classes you register here in a Model which are the
building blocks of tryton.
More on this later, for now let's break it down a bit.
from trytond.pool import Pool
Here we import the Pool
class from trytond. This class is central to the
tryton framework, because it allows for its full modularity. You will use it a
lot, we will see how later.
import library
This is a standard python import of the library.py
file directly in our
module folder. We need it to access the classes that we must register.
def register():
The __init__.py
file of a tryton module is expected to declare a
register
function. It will be called by the server during the server
start up to register the module.
Pool.register(
library.Author,
module='library', type_='model')
Here is where things get done. We are basically instructing tryton to
register the library.Author
class in the Pool
. We specify that this
class is linked to the library
module, and that it is a model
.
Warning: the module
argument value MUST be identical to the name of
your module as it is understood by tryton. There are different way to specify
this name, but the basic one is the name of the directory in which you are
working.
The type_
parameter have the following possible values: model
,
wizard
and report
. The latter is seldom use and will not be detailed in
this training, the second we will see later.
Note: The Pool.register
method accepts a list of classes as parameters,
so if your module defined more than one model
, they can be registered in
one go:
Pool.register(
MyModel1,
MyModel2,
MyModel3,
module='my_module', type_='model')
Python: in library.Author
, the library
part is not the module
name, but that of the library.py
file that we imported earlier
Style: Each model should be declared on a separate line
The order of declaration matters here, because models will be registered in the
order they are defined here. There are some rules regarding this order that
will be later explained, but a good rule of thumb is to go from configuration
/ abstraction to actual pure data models (BookConfiguration
before
Book
), and from container to containee (Author
before Book
).
Open the library.py
file and write the following:
from trytond.model import ModelSQL, ModelView
__all__ = [
'Author',
]
class Author(ModelSQL, ModelView):
'Author'
__name__ = 'library.author'
The python files in a tryton module will mainly be used to create classes which are used to describe your module's behavior. There are some rules regarding how such classes should be written, so let's dive in.
from trytond.model import ModelSQL, ModelView
The trytond.model
module is toolset that is used to create models in
tryton. Here we import two elements from it, the classes ModelSQL
and
ModelView
. Those are required to handle persistence of data (ModelSQL
)
and UI (ModelView
). Most models will inherits from both, some only from one
depending on their intended use.
__all__ = [
'Author',
]
Here we tell python which elements of our file will be immediately usable when
someone imports the library.py
file. It should contain all the tryton
models that are declared in the file.
Python: __all__
is a python keyword, and the elements in the list are
strings that match a top level class or method name
Style: One line per element in the list, in the order of definition of the file, with commas at the end of line
class Author(ModelSQL, ModelView):
'Author'
__name__ = 'library.author'
This is the building block of all tryton modules, declaring a new model. This
is done by creating a new class which inherits from ModelSQL
or
ModelView
. Or both, as python allows multiple inheritance, which is
extensively used in tryton.
Note: The class name Author
is not important, meaning you could use
anything from MyClass
to Foo
. However, it is strongly recommanded to
use an explicit name for better readability
The __doc__
attribute of the class (in our case Author
, defined by the
first string under the class declaration) is mandatory for new models. It
is used throughout the application as the default description of the model, and
will be displayed to the end users, so it should be both short and
descriptive.
The __name__
attribute is central to the tryton framework. This is what
will be used to identify and use your model all over the application. The class
name (Author
) is irrelevant, what matters is the value of __name__
.
Since the value is so important, it is very important that it is properly though through. There is no absolute rule, but you should follow those guidelines:
- Usually, the first part of the name (
library
) represents the functional domain of the model. It is not necessary the module's name, though if your module defines a new functional domain it may be. It is used to visually group models under a group to ease in-mind representation - Dots (
.
) are used to separate components of the name, from the more general to the more specific. You can think ofcar.wheel
as "Awheel
which is a part of acar
" - Only use alphanumeric characters and dots (
.
), dashes (-
) and underscores (_
)
It is important to think about the __name__
because this will usually be
the first information that you get about an instance when developing with
tryton. So a badly or wrongly named model will make it all the more difficult
to understand.
To sum it up, we defined a model that we want to be stored in the database and
displayed to the end user (since it inherits from ModelSQL
and
ModelView
), which will be later identified in tryton with the __name__
library.author
.
Note: We will talk a lot about "models" and "records" during this training.
You can think of "models" as tables in a database (at least for ModelSQL
models), and "records" as rows in those tables. Another way to look at it is
that "models" are classes, and "records" instances of those classes
Open the library.xml
file and write the following:
<?xml version="1.0"?>
<tryton>
<data>
<!-- ########## -->
<!-- # Author # -->
<!-- ########## -->
<record model="ir.ui.view" id="author_view_form">
<field name="model">library.author</field>
<field name="type">form</field>
<field name="name">author_form</field>
</record>
<record model="ir.ui.view" id="author_view_list">
<field name="model">library.author</field>
<field name="type">tree</field>
<field name="name">author_list</field>
</record>
</data>
</tryton>
That's a lot of things to write down.
A word on xml first (if you already know about it please go ahead). eXtended Markup Language is a structured language that is used to represent data structures. It is comparable to a more verbose and powerful JSON. You should read about the basics of xml if you do not know them.
XML is extensively used by tryton to define data that will be inserted in the database when installing / upgrading the module. As stated earlier, those data typically include UI related informations, access right management and configuration data.
The library.xml
file now contains some information.
<?xml version="1.0"?>
This is a standard declaration in xml files to identify the version of the xml parser to use, it should be used in all xml files you ever write for tryton.
<tryton>
</tryton>
The <tryton>
is required so that tryton knows where it should parse and
treat in the xml file. There should only be one such tag in your xml files.
<data>
</data>
The <data>
tag defines a group of records that share the same properties.
It has options
that are rarely used, we will not talk about them for now. Note however that
there may be multiple <data>
blocks in a given xml file, with different
options.
<!-- ########## -->
<!-- # Author # -->
<!-- ########## -->
When describing data in an xml file, it is good to regroup it by model for readability. Ideally, comment blocks should be used to identify those groups, and be able to visually identify them.
<record model="ir.ui.view" id="author_view_form">
<field name="model">library.author</field>
<field name="type">form</field>
<field name="name">author_form</field>
</record>
This is what most elements in a tryton xml file will look like. Let's break it into pieces:
<record model="ir.ui.view" id="author_view_form">
</record>
Here we are instructing tryton to create a new record
. The model
of
this record should be "ir.ui.view
" and its id "author_view_form
".
Remember what we said about record being like instances of a class, or rows in
a database ? We are basically creating a new instance of the model
ir.ui.view
, and assigning it an identifier, author_view_form
.
The ir.ui.view
model is part of the ir
module of tryton, and is the
representation of a view in the UI part of tryton. So we are basically defining
a new view to be used in tryton. The identifier author_view_form
must be
unique across the whole module, and should be as descriptive as possible. We
will detail this one later.
Almost all data that will be included in xml files will be elements with this structure:
<record model="my.model.__name__" id="my_unique_identifier">
<data goes here/>
</record>
Note: The identifier should only contain alphanumeric characters, as well as
dashes and underscores. No dots (.
) allowed !
So now, let's see the details of our view:
<field name="model">library.author</field>
<field name="type">form</field>
<field name="name">author_form</field>
A ir.ui.view
object has several fields that must be set (think columns in a
database).
- The
model
field contains the model for which the view is defined. The value here islibrary.author
, which matches the__name__
of the "Author" model that we defined in thelibrary.py
file earlier. This is the first example you will see of using the__name__
of a model to identify it when developing module for tryton - The
type
field defines the "type" (obviously) of the view. There are multiple values that can be used, but the most used areform
(for "one-instance" views) andtree
(for multiple instances views). The value here isform
, so this view will be used when showing one instance to the used - The
name
field is particular. It contains the name of a file that will be used to describe the contents and layout of the view. Those files are located under theview
folder of the module, and sure enough you should find aauthor_form.xml
file in it
So now the identifier author_view_form
should make more sense. It is a
form
view
used to display an author
.
Note: for ir.ui.view
records, the standard pattern for the identifier is
<model_identifier>_view_<view_type>
. The model_identifier
part is
usually a part of the __name__
of the model, and should be as explicit as
possible. However remember, no dots !
<record model="ir.ui.view" id="author_view_list">
<field name="model">library.author</field>
<field name="type">tree</field>
<field name="name">author_list</field>
</record>
You should be able to figure this one alone. Something should bother you however... Got it ?
For tree
views, the view type in the identifier should be tree
, but
here is a list
! The reason for this is that tryton use the tree
type
for both lists and trees (in case it is no self-explanatory, trees may have
nodes and sub-elements, while lists are flat and non-nested). So in order to
know which type is view really is, we compose the identifier accordingly.
Open the view/author_form.xml
and write the following:
<?xml version="1.0"?>
<form>
</form>
You should recognize the first line easily, it is the one we said earlier
should be the first line of all your xml files. The <form>
tag is used to
declare the form view, we will let it empty for now.
Open view/author_list.xml
and edit it the same way:
<?xml version="1.0"?>
<tree>
</tree>
You should now be able to install your module on a test database. If there is an error when doing so, check your code again.
If it still does not work, you can find its contents on the git branch
step1_completed
. You can compare against it by using the command:
git diff origin/4.4/step1_completed
This should show you the differences between your code and what was expected.
Once your module is installed, you unfortunately have no way to see it work for
now (since we did not entry points in the application to do so, though that
will come later). You can trust tryton that if the installation did not fail,
it worked. If you are paranoïd (or just curious), you can browse the database
you are using, and you should notice that a new table was added in there,
library_author
. This should be proof enough for now.
- Define four more models in the module, one for books, one for book
exemplaries, one for an editor, and one for a book "genre". Be extra
careful on how you name them (their
__name__
) and in which order you define them in the different files - Add views for those models, with
<record>
entries inlibrary.xml
and the associated files in the view folder
You can compare your code with that of the step1_homework
branch for
differences.
You can read here about what you should have done, and why.
You now know about a module structure, and the basics of defining a model in tryton. Next we are going to link our models together.