Skip to content

4.4 step3

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

Step 3 - Adding fields

We now have s basic structure and relations in that structure. You may update on the step3 to get a clean version:

git checkout 4.4/step3

We got a skeleton, time to flesh it up!

Generalities on fields

Models are used to store data. But for now, we got empty books and authors. We did created fields in the previous step. However the "motivation" for those fields was to define relations between models, not to store "flat" informations.

Before adding more information though, some rules on fields that you should always think about.

Field location

Data fields should be added on the model which is the more logical (the "Car color" field should be on the "Car" model). If it is not obvious, ask to one of your fellow developers, and in last resort choose the model which makes it easier for you to go on with your developments.

Inside a given model, the "data" field should be placed after the structural Many2One and One2Many fields, in their relative order of importance. For instance for a book, the title field should probably be located before the page_count field since the latter is not an information you will look for first when reading the book's data.

Field naming

Think carefully when naming your fields. The field name will in most case be the only information that you will have about its contents. You do not want to have to look its definition up in the source code to understand what it contains everytime you see it somewhere.

Ideally, a field name should in itself be enough to know:

  • The "type" of data it contains
  • The model it is about (for relation fields only)
  • The cardinality of its contents: make sure there is an "s" at the end of every Many2Many or One2Many field name
  • Use camelcase with underscores (_) as word delimiters
  • No articles (author rather then the_author)

Try to avoid abbreviations. If the field name you are thinking about is so long that abbreviations are required, you probably should think twice about it.

Data duplication

As much as possible, information duplication should be avoided. The main reason is consistency across the application: if we add a name field on our library.author model, and a author_name field on the library.book model, there could be the risk that they do not match for a given book. In that case there is always the question of which one is right, which one is wrong, what should be done, etc.

There are means to calculate the information in tryton, so when creating a data field, one should always check that the information does not already exist somewhere, or that it is not possible to compute it from existing informations.

  • Number of exemplaries of a book: Not a data field, it can be computed from the number of elements in the list of exemplaries
  • Name of an author: It's a data field, the information does not exist (yet) in any model
  • Name of a book's author: If the Name of on author field is defined, it can be computed, since the library.book model already has a author field
  • Genres of an author: Since authors write books, and that the library.book model has a genre field, it is possible to compute the genres an author writes from the books he has already written, so not a field

Note: You could (rightly) argue that the genres field we defined on the library.editor model could be computed from the books that were edited by the editor. It was easier to do so for this tutorial purpose, but it indeed should not be a data field

Common fields

Now that we are good, we are going to add some fields on our models, so that we are finally starting to have "real" informations.

Char fields

One of the most common field type is the Char field. Open library.py and add the following field in the library.author model, under the books field:

name = fields.Char('Name', required=True)

As you probably guessed, we add a new field named name, which is required.

Warning: Char fields are not "chars" but rather "strings", so not just single characters, but any string

Char fields are used to store "short" (i.e. less than a couple hundred characters) strings. It is possible to limit their size with the optional size attribute:

my_string = fields.Char('my_string', size=10)

Such a constraint will be enforced by the client, the server, and if it supports it the database.

Text fields

Char fields are great for short strings, but if what you want is a large text, you need something bigger. Add the following field in the library.book model:

summary = fields.Text('Summary')

Obviously, limiting a book summary to a couple hundred characters was going to be tricky...

There are two main differences between Char and Text fields:

  • Text fields are not limited in size, whether it is by the server or the database
  • Text fields are designed and displayed by default to properly manage multiline input. It may also use to hold formatted content (more on this later)

Integer fields

Another frequent field is Integer, which is used to store integers, positive or negative. Add the following under the library.book model:

page_count = fields.Integer('Page Count',
    help='The number of page in the book')

The help attribute, though we did not use it previously, is available on all field types. It is a string which will be displayed client side as a pop up when the user focuses the field. It is good to set it unless you are sure the field will never be displayed to the client, or it's obvious from the field's string.

Boolean fields

Boolean fields are "True" / "False" fields which are great to store options, or "Yes" / "No" questions. Under the previously added page_count field on the library.book model add the following:

edition_stopped = fields.Boolean('Edition stopped',
    help='If True, this book will not be printed again in this version')

As you will see in the next step, this field will be displayed to the user with a checkbox that can be ticked or not. Note as this field will greatly benefit from the help attribute because the field name cannot contain all the scope of its value in a reasonable number of characters.

Numeric fields

Integer are great for countable entities, but how can I store non-integer values ? Tryton provides the Numeric field for that. Add one in the library.book.exemplary model:

acquisition_price = fields.Numeric('Acquisition Price', digits=(16, 2))

The digits parameter is used to specify the precision with which we want to store the information. The first value (16) represents the maximum number of digits that will be used to store the value. So if a number exceeds 10^16, there will be some precision loss. You should usually use the default value, which is 16 and will work in most cases. The second value (2) is the maximum allowed number of digits after the comma. Here it is set to 2 because we assume that the library uses a 2-digits currency. This precision is checked by the client and the server, so trying to store an acquisition_price with more digits than allowed will cause an error.

Warning: Tryton also provides a Float field to store non-integer data. You should never use it, unless absolute precision in arithmetics is not required. See here for more informations

Date (and other time-related) fields

Storing the date of an event is a common requirement. Open the library.book.exemplary model and create the acquisition_date field:

acquisition_date = fields.Date('Acquisition Date')

Tryton provides other field types for most time information, though the most frequently used is by far the Date type:

  • Time: Stores the time of a day
  • DateTime: To store a Date plus Time
  • Timestamp: Stores a Unix timestamp as a datetime
  • TimeDelta: Store the interval between two datetimes

In case you need to use any of those, check the tryton documentation

Less common fields

Selection fields

Selection fields are enumerates. The field definition includes the possible values, which will limit user input, and be checked by the server before storage in the database. Add a new Selection field to the library.author model:

gender = fields.Selection([('man', 'Man'), ('woman', 'Woman')], 'Gender')

There are multiple variants on how to define the possible values of a Selection field, but for now we will stick to the simple version. The first parameter of the field may contain a list of tuples. The first item in each tuple will be the field value, and the second how it will be displayed to the user.

This is something important. Selection fields are actually stored the same way Char fields are, as strings in the database. When manipulating Selection field values in server code, we will actually use strings. Again, making sure that the fields values are properly named is essential for later readability.

Binary fields

If you want to store non-textual data in the database, you have to use Binary fields. Let's store the cover of our books:

cover = fields.Binary('Cover')

Done! Binary fields are not often used, and accept two storage mode:

  • DB storage (the default one, which we used here), where the contents will be stored as is in the database
  • Filesystem storage, where the contents are stored on the server filesystem, and the database only contains a key which allows to find it later. This form is interesting if you have numerous large files you do not want to bother your database with

Reference fields

These fields are non-specified Many2One fields. Non-specified, because a Many2One field's first parameter is the name of the model you want to link to, but Reference fields do not need this. They allow a field to hold a reference to any model which is allowed by the developer.

A typical use case would be a field on a logging model, in which you want to reference the record that triggered the log. If you are sales, you may have a car model, a bike model, and a truck model. When you sale them, you want to write it down in a dedicated table, with a reference to the particular item you sold. You cannot use a Many2One field here, because then you would have to either have three of those (one for each possible type of vehicle you sale), or only log the information for one of those. Reference fields will allow you to do all at once, but at a cost.

Many2One fields integrity is enforced at the database level. The ondelete parameter allows to control the behavior when removing entries from the database. There is no such thing for Reference fields. You may delete a record which is referenced somewhere, with no control. So you can end up with "holes" in your database.

One must be very careful when creating Reference fields.

  • Make sure there is no other way to store the information, maybe tweaking the model a little
  • Do manual checks on record deletions to check if they are used in your reference field
  • Only use it for "expandable" data. Losing a reference in a log is bothersome, but it will not break your application. Not knowing the person who last borrowed a book that is missing can

Dict fields

Dict fields are great if you need to allow your users to customize your application. With them, you can define a dedicated model to hold "questions". Then, you can add a Dict fields which will prompt the user for the answer to those questions, record by record.

These fields will be explained more thoroughly later in the training module.

Other field types

There are a few other field types that are available in tryton, though they are rarely used:

  • BigInteger are here when standard Integer fields are not big enough to store your values
  • Float are inaccurate versions of Numeric, which can be considered if absolute precision is not a requirement
  • One2One fields are Many2Many fields with a size 1 limit. They are a Many2Many because they use an additional model between the elements to link together, but each linked element may only be linked once. An exemple would be the relation between network ports. A given port can be linked to any other port (using an Ethernet cable), but it can be only linked to one port at a time. It could be implemented with symmetrical Many2One fields, but there would be the risk of inconsistency between relations.
  • MultiValue may be used for advanced configuration, where you typically want to store multiple values for a given key, and the good one will be found with pattern matching

Read the tryton documentation on those fields if you need to use them

Function fields

Function fields are critical to tryton development, and will be covered separately later.

Field parameters

We already talked about some optional parameters of fields (required, help, etc.), here are the main basic options:

  • required: Makes the fields required at the client, server and database level
  • readonly: Makes the field readonly, but only client-side. It is always possible to modify the field value when writing server code, only the user is limited. If a field should absolutely not be modified, there are other solutions to check for this. Setting it readonly will only make it more difficult for the end user to do so
  • help: Helps the user by adding a descriptive text which will appear in the UI when hovering over the field widget
  • select: A little more on the advanced side, this parameter is a boolean that automates the creation of an index on the field to improve performances if this field is often used for searching

There are other common parameters, and some parameters that are specific to some field types (ondelete, etc.), we will cover most of those later.

Homework

You are going to add fields to our models. For each of them, ask yourself for the name it should have, if it is really needed (do not add it if it is not!), its parameters (should it be required ? readonly ? should I add some help ?).

If you feel that a given field should only be required / readonly under certain circumstances, note that information somewhere, we will come to this eventually.

Here are the fields that you should add:

  • The "name" of a genre
  • The "name" of an editor
  • The "title" of a book
  • The date at which an editor started its activity
  • The number of books an author wrote
  • The birth date of an author
  • The death date of an author
  • A one-line description of a book
  • An identifier on an exemplary

You can compare your code with that of the step3_homework branch for differences. You can then read here about what you should have done, and why.

What's next

Our model is now fully-fledged, we will now add a way to create some data in the database through the user interface.