Skip to content

4.4 step1

Jean Cavallo edited this page Jan 22, 2019 · 14 revisions

Step 1 - Module Basics

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.

Module structure

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.

The tryton.cfg file

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

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

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

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

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.

The __init__.py file

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).

Model definition

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 of car.wheel as "A wheel which is a part of a car"
  • 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

XML data

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 is library.author, which matches the __name__ of the "Author" model that we defined in the library.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 are form (for "one-instance" views) and tree (for multiple instances views). The value here is form, 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 the view folder of the module, and sure enough you should find a author_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.

View files

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>

Testing it all

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.

Homework

  • 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 in library.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.

What's next

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.