Series: Odoo 18 Development for Python Developers Target Audience: Python developers who are new to web development and want to learn Odoo Prerequisites: Basic Python knowledge (functions, classes, OOP, pip)
What is Odoo? ERP, Framework, or Both?
If you come from the Python world, you probably know Django (a web framework) and you might know SAP or Oracle (ERP systems). Odoo sits in a unique position — it is both.
Odoo as an ERP (Enterprise Resource Planning)
Out of the box, Odoo gives you a full suite of business applications:
- Sales — manage quotations, orders, and invoicing
- Inventory — track products, warehouses, and shipping
- Accounting — handle invoices, payments, and financial reports
- HR — manage employees, payroll, and recruitment
- CRM — track leads and customer relationships
- Website & eCommerce — build websites and online stores
Think of it like this: if a company needs software to run their business, Odoo can handle almost everything — from sending an invoice to managing a warehouse.
Odoo as a Framework
Here’s where it gets interesting for us as developers. Beneath all those business applications, Odoo is a Python web framework — similar in concept to Django or Flask, but with a very different philosophy.
Odoo provides:
- An ORM (Object-Relational Mapping) to interact with the database using Python classes
- A view system that renders UI from XML definitions (no HTML/CSS templates needed for standard views)
- A module system that lets you install, uninstall, and upgrade features independently
- A security layer for access control
- A web client built with JavaScript (using their own framework called OWL)
The key insight: When we “develop in Odoo,” we are not building standalone Python apps. Instead, we are writing modules (think: plugins) that extend or customize the Odoo framework. Our Python code plugs into Odoo’s existing architecture.
Odoo Architecture Overview
Before writing any code, let’s understand how the pieces fit together. If you’ve never worked with web development before, don’t worry — we’ll start from the basics.
Client-Server Architecture
Odoo follows a client-server model. Here’s what that means in simple terms:
┌─────────────────┐ HTTP ┌──────────────────────┐
│ │ ◄──────────────────► │ │
│ Web Browser │ (JSON-RPC) │ Odoo Server │
│ (Client) │ │ (Python) │
│ │ │ │
└─────────────────┘ └──────────┬───────────┘
│
│ SQL
▼
┌──────────────────────┐
│ │
│ PostgreSQL │
│ (Database) │
│ │
└──────────────────────┘
Let’s break this down:
- Web Browser (Client): This is what the user sees. Odoo’s entire user interface runs in the browser. It’s a Single Page Application (SPA) built with JavaScript.
- Odoo Server (Python): This is where YOUR code lives. It handles business logic, processes requests from the browser, and talks to the database. The server is a Python application.
- PostgreSQL (Database): Odoo exclusively uses PostgreSQL. Not MySQL, not SQLite — only PostgreSQL. All your data (customers, products, invoices, etc.) is stored here.
How a Request Flows
When a user clicks a button in the browser, here’s what happens:
- The browser sends a JSON-RPC request to the Odoo server (think of it like an API call)
- The Odoo server receives the request and routes it to the appropriate Python method
- That Python method uses the ORM to read/write data in PostgreSQL
- The server sends a JSON response back to the browser
- The browser updates the UI
As an Odoo developer, most of your work happens at step 2 and 3 — writing Python methods that contain business logic and interact with the database through the ORM.
The Three Layers
You can think of Odoo as having three layers:
| Layer | Technology | Your Role |
|---|---|---|
| Data Layer | PostgreSQL + Odoo ORM | Define models (tables) in Python. The ORM creates and manages the actual database tables for you. |
| Logic Layer | Python | Write business logic as methods on your models. |
| Presentation Layer | XML (views) + JavaScript (OWL) | Define how data is displayed using XML. For advanced UI, write JavaScript components. |
Important for Python developers: You almost never write raw SQL. The ORM handles database operations. You also rarely write HTML — the UI is defined in XML, and Odoo renders it for you.
How Odoo Differs from Django/Flask — Mental Model Shift
If you’ve used Django or Flask (even briefly), understanding the differences will help you avoid confusion. If you haven’t used them, skip to the comparison table — it still helps frame how Odoo works.
In Django/Flask, You Build From Scratch
In a typical Django project:
- You define models (database tables) in
models.py - You write views (request handlers) in
views.py - You create HTML templates with Jinja2 or Django Template Language
- You configure URLs in
urls.py - You build your app from the ground up
In Odoo, You Extend an Existing System
In Odoo:
- You define models in Python (similar to Django), but they plug into Odoo’s ORM
- The UI is defined in XML, not HTML templates
- You don’t write URL routes — Odoo’s web client handles routing
- You inherit and extend existing models instead of building from scratch
- You organize everything into modules that can be installed/uninstalled
Comparison Table
| Concept | Django/Flask | Odoo |
|---|---|---|
| Project structure | You decide | Convention-based module structure |
| Database ORM | Django ORM / SQLAlchemy | Odoo ORM (unique to Odoo) |
| Database | Any (SQLite, MySQL, PostgreSQL) | PostgreSQL only |
| UI rendering | HTML templates (Jinja2) | XML view definitions |
| URL routing | urls.py / @app.route |
Automatic (actions + menus) |
| Admin interface | Django Admin (optional) | Built-in (it IS the interface) |
| Authentication | You configure it | Built-in user/group system |
| Package manager | pip | Odoo module system |
| Philosophy | “Build what you need” | “Extend what exists” |
The Biggest Mental Shift
Django/Flask: “I’m building an application.” Odoo: “I’m extending a platform.”
In Odoo, you rarely start from zero. Need a customer management system? Don’t build one — install the Contacts module and extend it. Need invoicing? Install the Accounting module and customize it.
Your job as an Odoo developer is often to:
- Install existing modules
- Extend their models with new fields
- Customize their views with new UI elements
- Override their business logic with your own rules
This is a powerful approach, but it requires learning how Odoo’s inheritance system works (we’ll cover this in Lesson 9).
Odoo Editions: Community vs Enterprise
Odoo comes in two editions. As a developer, you need to understand the difference:
Community Edition (CE)
- License: LGPL v3 (open source, free)
- Source code: Available on GitHub
- Includes: Core framework, basic modules (Sales, CRM, Inventory, etc.)
- Best for: Learning, development, small businesses, custom projects
Enterprise Edition (EE)
- License: Proprietary (paid subscription)
- Includes: Everything in CE + advanced modules (Full Accounting, Studio, IoT, etc.)
- Best for: Production use by medium/large businesses
Which One Should You Use for Learning?
Community Edition. It’s free, open source, and contains everything you need to learn Odoo development. All the core concepts — ORM, views, inheritance, security — are identical in both editions.
The Enterprise Edition mainly adds more business modules and some advanced features. The development patterns are the same.
Throughout this series, we will use Odoo 18 Community Edition.
Key Terminology: Modules, Models, Views, Actions, Menus
Before going further, let’s define the five terms you’ll encounter in every single Odoo lesson:
Module
A module is a self-contained package of functionality. Think of it like a “plugin” or “app.”
- Each module lives in its own directory
- It has its own models, views, security rules, and data
- It can be installed and uninstalled independently
- It can depend on other modules
Examples of built-in modules: sale (Sales), stock (Inventory), account (Accounting).
Model
A model is a Python class that represents a database table. If you know Django, it’s the same concept as a Django model.
# This is an Odoo model — it creates a table in PostgreSQL
from odoo import models, fields
class Book(models.Model):
_name = 'library.book' # This becomes the table name (with dots replaced by underscores)
_description = 'Library Book' # Human-readable description
name = fields.Char(string='Title', required=True) # A text column
pages = fields.Integer(string='Number of Pages') # An integer column
is_available = fields.Boolean(string='Available?') # A boolean column
What’s happening here:
_name = 'library.book'— This tells Odoo to create a table calledlibrary_bookin PostgreSQLfields.Char(...)— Creates a VARCHAR column. Thestringparameter is the label shown in the UIrequired=True— This field cannot be empty (likeNOT NULLin SQL)- You don’t write
CREATE TABLE— the ORM does it automatically when you install the module
View
A view is an XML definition that describes how a model’s data is displayed in the browser. Odoo supports several view types:
- Form view — for viewing/editing a single record (like a detail page)
- List view — for viewing multiple records in a table (like a spreadsheet)
- Kanban view — for viewing records as cards in columns (like Trello)
- Search view — for defining filters and search options
<!-- This is a simple list view — it tells Odoo to show name and pages as columns -->
<record id="library_book_list" model="ir.ui.view">
<field name="name">Book List</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<list>
<field name="name"/> <!-- Shows the "Title" column -->
<field name="pages"/> <!-- Shows the "Number of Pages" column -->
<field name="is_available"/> <!-- Shows the "Available?" column -->
</list>
</field>
</record>
What’s happening here:
— This is how you create data in Odoo’s XML files. Here we’re creating a view recordmodel="ir.ui.view"— We’re creating a record in their.ui.viewtable (where all views are stored)— This view is for thelibrary.book library.bookmodel— This defines a list (table) view— Eachtag adds a column to the list
Don’t worry about memorizing this syntax now. We’ll go deep into views in Lessons 6 and 7.
Action
An action tells Odoo what to do when something is clicked. The most common type is a window action, which opens a view for a model.
<!-- This action says: "Open the list of books" -->
<record id="library_book_action" model="ir.actions.act_window">
<field name="name">Books</field>
<field name="res_model">library.book</field> <!-- Which model to display -->
<field name="view_mode">list,form</field> <!-- Show list view first, form view on click -->
</record>
What’s happening here:
model="ir.actions.act_window"— This is a “window action” (the most common action type)res_model— The model this action targetsview_mode— A comma-separated list of view types.list,formmeans: show a list first, and when the user clicks a record, open the form view
Menu
A menu item creates an entry in Odoo’s navigation menu. It points to an action.
<!-- This creates a menu entry that triggers the book action -->
<menuitem
id="library_book_menu"
name="Books"
action="library_book_action"
sequence="10"
/>
What’s happening here:
id— A unique identifier for this menu itemname— The text shown in the navigation menuaction— Which action to trigger when clicked (references the action we defined above)sequence— The order in the menu (lower numbers appear first)
How They Connect
Here’s the full chain:
User clicks Menu → Menu triggers Action → Action opens View → View displays Model data
This is the fundamental flow of Odoo’s UI. Understanding this chain is essential.
The Odoo Module as the Unit of Everything
In Django, your project can be structured however you want. In Odoo, everything is a module, and every module follows a specific structure.
Here’s what a typical Odoo module looks like:
library_app/ # Module directory (this is your module's root)
├── __init__.py # Python package init — imports sub-packages
├── __manifest__.py # Module metadata — name, version, dependencies
├── models/ # Your Python model files
│ ├── __init__.py
│ └── book.py # Defines the library.book model
├── views/ # Your XML view files
│ └── book_views.xml # Form, list, search views for books
├── security/ # Access control files
│ └── ir.model.access.csv # Who can read/write/create/delete
├── data/ # Default data loaded on install
│ └── book_data.xml
├── demo/ # Demo data (only loaded in demo mode)
│ └── book_demo.xml
└── static/ # Static assets (icons, JS, CSS)
└── description/
└── icon.png # Module icon shown in the Apps menu
Key points:
- The directory name (
library_app) is your module’s technical name __manifest__.pyis the most important file — it tells Odoo everything about your module- The
models/,views/,security/structure is a convention, not a requirement — but everyone follows it __init__.pyfiles are required (just like regular Python packages) so Odoo can import your code
We’ll build this structure step by step in Lesson 2.
Setting Up Your First Odoo 18 Development Environment
Let’s get our hands dirty. We’ll set up Odoo 18 on your machine so you can follow along with the rest of the series.
We’ll cover two methods. Choose the one that suits you best:
- Method A: Docker (recommended — clean, reproducible, no system pollution)
- Method B: Source Install (more control, closer to production setup)
Method A: Docker Setup (Recommended)
Docker lets you run Odoo without installing anything on your system except Docker itself.
Step 1: Install Docker
If you don’t have Docker installed, download it from docker.com.
Verify installation:
docker --version
# Expected output: Docker version 24.x or higher
docker compose version
# Expected output: Docker Compose version v2.x or higher
Step 2: Create the Project Structure
# Create a directory for your Odoo project
mkdir odoo18-dev
cd odoo18-dev
# Create a directory for your custom modules
mkdir custom-addons
Step 3: Create the Docker Compose File
Create a file called docker-compose.yml in the odoo18-dev directory:
# docker-compose.yml
# This file defines two services: the Odoo server and the PostgreSQL database
version: '3.8'
services:
# PostgreSQL database service
db:
image: postgres:16 # Use PostgreSQL 16 (recommended for Odoo 18)
container_name: odoo18-db
environment:
POSTGRES_DB: postgres # Default database
POSTGRES_USER: odoo # Database user (Odoo will connect with this)
POSTGRES_PASSWORD: odoo_password # Database password (change in production!)
ports:
- "5433:5432" # Map to port 5433 to avoid conflicts with local PostgreSQL
volumes:
- odoo-db-data:/var/lib/postgresql/data # Persist database data
# Odoo server service
odoo:
image: odoo:18 # Official Odoo 18 Docker image
container_name: odoo18-server
depends_on:
- db # Wait for database to start first
ports:
- "8069:8069" # Odoo web interface
- "8072:8072" # Odoo live chat / longpolling
environment:
HOST: db # Connect to the 'db' service
USER: odoo # PostgreSQL user
PASSWORD: odoo_password # PostgreSQL password
volumes:
# Mount your custom modules directory into the container
- ./custom-addons:/mnt/extra-addons
# Named volume to persist database data across container restarts
volumes:
odoo-db-data:
What each line does:
image: postgres:16— Downloads and runs PostgreSQL 16 in a containerimage: odoo:18— Downloads and runs the official Odoo 18 imagedepends_on: db— Ensures the database starts before Odooports: "8069:8069"— Makes Odoo accessible athttp://localhost:8069volumes: ./custom-addons:/mnt/extra-addons— Maps your localcustom-addonsfolder into the container, so Odoo can see your modules
Step 4: Start Odoo
# Start both services in the background
docker compose up -d
# Check that both containers are running
docker compose ps
You should see both odoo18-db and odoo18-server in “running” state.
Step 5: Access Odoo
Open your browser and go to: http://localhost:8069
You’ll see the database creation page. Fill in:
- Master Password:
admin(this is the Odoo master password, not the database password) - Database Name:
odoo18dev - Email:
admin@example.com - Password:
admin(this will be your login password) - Language: English
- Country: Choose your country
- Check “Demo data” — this loads sample data which is useful for learning
Click “Create database” and wait. This may take 1-2 minutes.
Useful Docker Commands
# View Odoo logs (useful for debugging)
docker compose logs -f odoo
# Stop all services
docker compose down
# Stop and remove all data (fresh start)
docker compose down -v
# Restart Odoo after code changes
docker compose restart odoo
Method B: Source Install (Ubuntu/Debian)
This method installs Odoo directly from source code. It gives you more control and is closer to a production setup.
Step 1: Install System Dependencies
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install Python 3.12 and development tools
sudo apt install -y python3.12 python3.12-venv python3.12-dev
# Install PostgreSQL
sudo apt install -y postgresql postgresql-client
# Install other system dependencies required by Odoo
sudo apt install -y \
git \
build-essential \
libxml2-dev \
libxslt1-dev \
libldap2-dev \
libsasl2-dev \
libjpeg-dev \
zlib1g-dev \
libfreetype6-dev \
liblcms2-dev \
libopenjp2-7-dev \
libtiff5-dev \
node-less \
npm \
wkhtmltopdf
What are these packages?
python3.12— Odoo 18 requires Python 3.10+ (3.12 is recommended)postgresql— The database enginegit— To clone the Odoo source code- The
lib*-devpackages — C libraries needed to compile Python packages likePillow(image processing) andlxml(XML parsing) wkhtmltopdf— Converts HTML to PDF (used for printing reports)node-lessandnpm— For compiling CSS assets
Step 2: Configure PostgreSQL
# Create a PostgreSQL user for Odoo (same name as your system user)
sudo -u postgres createuser --createdb --no-superuser --no-createrole $USER
# Verify it works
psql -d postgres -c "SELECT version();"
Why --createdb? Odoo needs permission to create new databases (each Odoo instance uses its own database).
Step 3: Clone Odoo Source Code
# Create a workspace directory
mkdir ~/odoo18-workspace
cd ~/odoo18-workspace
# Clone Odoo 18 (community edition) — this may take a few minutes
git clone https://github.com/odoo/odoo.git --branch 18.0 --depth 1
# Create a directory for your custom modules
mkdir custom-addons
What the flags mean:
--branch 18.0— Clone only the 18.0 branch (not the entire history of all branches)--depth 1— Shallow clone (only the latest commit, saves time and disk space)
Step 4: Set Up Python Virtual Environment
# Create a virtual environment
python3.12 -m venv odoo-venv
# Activate it
source odoo-venv/bin/activate
# Install Odoo's Python dependencies
pip install -r odoo/requirements.txt
# Install Odoo in development mode
pip install -e odoo
Why a virtual environment? It keeps Odoo’s Python packages separate from your system Python. This prevents version conflicts.
Step 5: Create a Configuration File
Create a file called odoo.conf in your workspace:
# ~/odoo18-workspace/odoo.conf
[options]
; Path to your custom modules (add more paths with commas)
addons_path = ./odoo/addons,./custom-addons
; Database settings
db_host = localhost
db_port = 5432
db_user = your_username
db_password = False
; Server settings
http_port = 8069
admin_passwd = admin
; Development settings
dev_mode = reload,xml
; Logging
log_level = info
Important settings explained:
addons_path— Where Odoo looks for modules. First path is Odoo’s built-in modules, second is your custom modulesdb_user— Replaceyour_usernamewith your actual system usernamedb_password = False— If your PostgreSQL user has no password (local peer authentication)dev_mode = reload,xml— Auto-restarts the server when Python files change, and reloads XML without restart. This is a huge time-saver during development.
Step 6: Start Odoo
# Make sure your virtual environment is activated
source odoo-venv/bin/activate
# Start Odoo with your config file
python odoo/odoo-bin -c odoo.conf
You should see log output ending with something like:
INFO odoo18dev odoo.service.server: HTTP service (werkzeug) running on 0.0.0.0:8069
Now open http://localhost:8069 in your browser and create your database (same steps as Docker Method, Step 5).
Running Odoo, Accessing the Web Client, Developer Mode
The Odoo Web Client
After creating your database and logging in, you’ll see the Odoo home screen with a grid of application icons. This is the Apps page.
Take a moment to explore:
- Click on Settings (gear icon) — this is where you configure Odoo
- Click on Apps — this shows all available modules you can install
- The top menu bar changes depending on which app you’re using
Activating Developer Mode
Developer Mode (also called Debug Mode) is essential for Odoo development. It unlocks:
- Technical menus (see database structure, view definitions, etc.)
- Field information on hover
- Access to edit views directly in the UI
- Technical settings and menus
How to activate it:
Method 1: Via Settings
- Go to Settings (main menu)
- Scroll to the bottom of the page
- Click “Activate the developer mode”
Method 2: Via URL (Faster)
Add ?debug=1 to your URL:
http://localhost:8069/web?debug=1
For Developer Mode with Assets (also loads unminified JavaScript — useful for JS debugging):
http://localhost:8069/web?debug=assets
What Changes in Developer Mode?
Once activated, you’ll notice:
- A debug icon (bug icon) appears in the top menu bar
- Technical menus appear under Settings → Technical
- Field tooltips — hover over any field in a form to see its technical name, type, and model
- Edit View — a small “Edit View” link appears on views, letting you see the XML definition
The Technical menu (Settings → Technical) is incredibly useful. It lets you browse:
- Models and their fields
- Views and their XML
- Actions and menus
- Security groups and access rules
- Sequences, email templates, and much more
The Odoo Shell
Odoo provides an interactive Python shell that gives you direct access to the ORM. This is very useful for testing and debugging.
# Docker method:
docker compose exec odoo odoo shell -d odoo18dev
# Source method:
python odoo/odoo-bin shell -c odoo.conf -d odoo18dev
Once in the shell, you can interact with the database:
# Search for all installed modules
modules = env['ir.module.module'].search([('state', '=', 'installed')])
print(len(modules)) # Number of installed modules
# Read a specific partner (customer/contact)
admin = env['res.partner'].browse(1)
print(admin.name) # Output: "YourCompany"
# Search for partners
partners = env['res.partner'].search([('is_company', '=', True)])
for p in partners:
print(p.name)
What’s happening here:
env— This is the Odoo Environment object. It gives you access to all modelsenv['ir.module.module']— Access the “Module” model (likefrom myapp.models import Module).search([('state', '=', 'installed')])— Search with a domain filter (we’ll cover domains in detail in Lesson 4).browse(1)— Get the record with ID = 1
Don’t worry if this looks unfamiliar. We’ll spend three full lessons on the ORM (Lessons 3, 4, and 5).
Summary & What’s Next
Let’s recap what we learned in this lesson:
Key Takeaways
- Odoo is both an ERP and a framework. As developers, we write modules that plug into the framework.
- Odoo uses a client-server architecture: browser (JavaScript) ↔ server (Python) ↔ database (PostgreSQL).
- The mental model shift: In Django/Flask, you build from scratch. In Odoo, you extend what exists.
- Community Edition is free and open source — perfect for learning and development.
- Five core concepts: Module → Model → View → Action → Menu. They connect like a chain: Menu triggers Action, Action opens View, View displays Model data.
- Everything is a module. Your code lives in modules with a specific directory structure.
- Developer Mode unlocks technical menus and debugging tools. Always have it on during development.
Quick Reference Card
| What | Where |
|---|---|
| Odoo Web Client | http://localhost:8069 |
| Developer Mode | Settings → Activate developer mode, or ?debug=1 |
| Your Custom Modules | ./custom-addons/ (Docker) or ~/odoo18-workspace/custom-addons/ (Source) |
| Odoo Logs | docker compose logs -f odoo (Docker) or terminal output (Source) |
| Odoo Shell | docker compose exec odoo odoo shell -d odoo18dev |
| Odoo Source Code | github.com/odoo/odoo branch 18.0 |
What’s Next?
In Lesson 2: Anatomy of an Odoo Module, we’ll create our first module from scratch. You’ll learn:
- The exact purpose of every file in a module
- How
__manifest__.pyworks - How
__init__.pychains work in Odoo - How to scaffold, install, and upgrade your module
Make sure your Odoo 18 environment is up and running before moving to Lesson 2!
Exercise: Before moving on, try these tasks to make sure your setup is working: 1. Start Odoo and create a database with demo data 2. Log in and activate Developer Mode 3. Go to Settings → Technical → Database Structure → Models and browse the list 4. Open the Odoo Shell and run:
env['res.partner'].search_count([])— this should return the number of contacts in your database 5. Install the “Sales” module from the Apps menu and explore the UI it creates
Next lesson: Lesson 2 — Anatomy of an Odoo Module
