Odoo Custom Module Development Part 2 - Models and Fields

This is the second article in the Odoo custom module development series.

In Part 1, we created the estate module skeleton and made Odoo recognize it. In this article, we will create our first Odoo model and add useful fields.

Goal of this article

We want to create a model named estate.property that stores:

  • property name
  • description
  • expected price
  • selling price
  • postcode
  • availability date
  • active flag
  • state

1. Create the model file

Inside the estate module, create a Python file:

estate/
models/
property.py

Add this to models/__init__.py:

from . import property

Now create models/property.py:

from odoo import fields, models


class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'Real Estate Property'
_order = 'id desc'

name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
active = fields.Boolean(default=True)
state = fields.Selection(
selection=[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('canceled', 'Canceled'),
],
required=True,
copy=False,
default='new',
)

2. Understand what this code does

Important pieces:

  • _name: the technical model name in Odoo
  • _description: readable model description
  • fields.Char(): short text
  • fields.Text(): long text
  • fields.Float(): decimal numeric field
  • fields.Boolean(): true or false
  • fields.Selection(): dropdown values

The model name must be unique across Odoo.

3. Upgrade the module

Whenever you add or modify models, upgrade the module so Odoo updates the database schema.

From the Odoo source directory:

cd ~/odoo-dev/odoo
source .venv/bin/activate
python3 odoo-bin -c ~/odoo-dev/odoo.conf -d odoo19 -u estate --stop-after-init

If the command finishes without errors, the database table for estate.property has been created.

4. How Odoo maps the model to the database

Odoo automatically creates a PostgreSQL table for the model. For estate.property, the generated table name is usually:

estate_property

You do not need to create SQL tables manually for standard models. Odoo’s ORM handles that for you.

5. Add default values for garden fields

If a property has a garden, it often makes sense to provide default values. Update the model like this:

from odoo import fields, models


class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'Real Estate Property'
_order = 'id desc'

name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer(default=10)
garden_orientation = fields.Selection(
[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
default='north',
)
active = fields.Boolean(default=True)
state = fields.Selection(
[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('canceled', 'Canceled'),
],
required=True,
copy=False,
default='new',
)

Upgrade the module again after editing.

6. Add a second model for property types

Most real estate systems need property categories such as apartment, villa, or office. Create another model file named property_type.py.

Add this line to models/__init__.py:

from . import property_type

Create models/property_type.py:

from odoo import fields, models


class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'Real Estate Property Type'
_order = 'name'

name = fields.Char(required=True)

Upgrade the module again.

7. Good model design habits

When defining models, keep these practices in mind:

  • use clear technical model names like estate.property
  • make required business data explicitly required=True
  • use copy=False for values that should not duplicate to copied records
  • give sensible defaults where it improves the data entry workflow
  • use _order to make record listings predictable

8. Common mistakes

You edited Python files but nothing changed

You probably forgot to upgrade the module:

python3 odoo-bin -c ~/odoo-dev/odoo.conf -d odoo19 -u estate --stop-after-init

A field type was changed and Odoo throws an error

Changing a field type after data already exists can require a migration. During early development, it is often faster to use a fresh test database.

Odoo says the model does not exist

Check:

  • the file is imported in models/__init__.py
  • the module itself is imported in the root __init__.py
  • the Python syntax is valid

The root __init__.py should contain:

from . import models

Final words

You now have real models and fields managed by Odoo’s ORM.

In the next article, we will add security so users can actually access these models safely.

Previous article: Part 1 - Create Your First Module

Next article: Part 3 - Security and Access Rights

Related posts

Md. Monirul Alom

Md. Monirul Alom

I am a Full Stack Web developer. I love to code, travel, do some volunteer work. Whenever I get time I write for this blog