Creating the data model
The Odoo development guidelines state that the Python files for models should be placed inside a models subdirectory. So, we will create a models/todo_task_model.py file in the main directory of the todo_app module.
Before that, we need to let Python know that the models directory should be used (imported). To do that, edit the module's main __init__.py file like this:
from . import models
We also need to add a models/__init__.py file, importing the Python code file to be used:
from . import todo_task_model
Now we can create the models/todo_task_model.py file with the following content:
from odoo import fields, models
class TodoTask(models.Model): _name = 'todo.task' _description = 'To-do Task' name = fields.Char('Description', required=True) is_done = fields.Boolean('Done?') active = fields.Boolean('Active?', default=True)
user_id = fields.Many2one(
'res.users',
string='Responsible',
default=lambda self: self.env.user)
team_ids = fields.Many2many('res.partner', string='Team')
The first line is a special marker telling the Python interpreter that this file has UTF-8 so that it can expect and handle non-ASCII characters. We won't be using any, but it's good practice to have it anyway.
The second line is a Python code import statement, making the models and fields objects from the Odoo core available.
The third line declares our new model. It's a class derived from models.Model.
The next line sets the _name attribute, defining the identifier that will be used throughout Odoo to refer to this model. Note that the actual Python class name, TodoTask in this case, is meaningless to other Odoo modules. The _name value is what will be used as an identifier.
Notice that this and the following lines are indented. If you're not familiar with Python, you should know that this is important: indentation defines a nested code block, so these four lines should all be equally indented.
Then we have the _description model attribute. It is not mandatory, but it provides a user-friendly name for the model records, which can be used for better user messages.
The remaining lines define the model's fields. It's worth noting that name and active are special field names. By default, Odoo will use the name field as the record's title when referencing it from other models. The active field is used to activate records, and by default, only active records will be shown. We will use it to clear away completed tasks without actually deleting them from the database.
We can see also examples of how to add fields with relationships to other models. The user_id field allows for picking the User owner of the Task. We added a default to it, so that the current user is automatically set as responsible for the Task.
Finally, team_ids allows us to select a list of Partners involved in the Task. It is a many-to-many relation, where each Task can have many Team members, and each Partner can be involved in many Tasks.
That's it! For our Python code changes to take effect, the module needs to be upgraded, triggering the creation of the corresponding objects in the database.
We won't see any menu options to access this new model since we didn't add them yet. Still, we can inspect the newly created model using the Technical menu. In the Settings top menu, go to Technical | Database Structure | Models, search for the todo.task model in the list, and click on it to see its definition:
If everything goes right, it is confirmed that the model and fields were created. If you can't see them here, try a server restart with a module upgrade, as described before.
We can also see some additional fields we didn't declare. These are reserved fields Odoo automatically adds to every new model. They are as follows:
- id is a unique numeric identifier for each record in the model.
- create_date and create_uid specify when the record was created and who created it, respectively.
- write_date and write_uid confirm when the record was last modified and who modified it, respectively.
- __last_update is a helper that is not actually stored in the database. It is used for concurrency checks.