---
url: https://docs.cybrosys.com/assista-ide/snippets/python-snippets.md
description: >-
  Insert Odoo models, fields, methods, errors, notifications and actions by
  typing a short keyword in a Python file. Snippets follow your Odoo version
  (18, 19 and 20).
---

# Python snippets

Type `odoo` and a few words in a Python file, then pick a snippet to insert ready-made Odoo code. Snippets follow the Odoo version of your project, so the code you insert uses the right syntax.

These snippets come from the bundled Odoo Helper and support Odoo 18, 19 and 20.

## What you can do

* **Insert a model class** with the right base class and constraints for your version.
* **Add a field or a method** with the common arguments already filled in.
* **Raise errors and show notifications** with a ready-made block.
* **Add the usual imports** with one keyword.
* **Move between placeholders** with `Tab`.

## Get started

1. Open a Python file in an Odoo module.
2. Type `odoo` and the name of what you need, for example `odoo new model`.
3. Pick the snippet from the list. Use the arrow keys to move, press `Enter` or `Tab` to insert it, and press `Esc` to close the list.
4. Press `Tab` to jump to each placeholder and fill it in.

Check the version in the status bar, for example `Odoo v20 (Auto)`. Select it to change it. See [Create modules and files](/assista-ide/module-creation) for how the version is detected.

## Models

| Snippet | What it inserts | Odoo 18 | Odoo 19 | Odoo 20 |
| :--- | :--- | :-: | :-: | :-: |
| `odoo new model class` | A model with `_name`, `_description` and a `name` field. On Odoo 20 it also has `_order`, an `active` field and a unique-name constraint. | Yes | Yes | Yes |
| `odoo classical inherited model class` | A new model that copies another with `_name` and `_inherit`. | Yes | Yes | Yes |
| `odoo delegated inherited model class` | A model that embeds a parent record with `_inherits` and a `Many2one` link. | Yes | Yes | Yes |
| `odoo extended inherited model class` | An extension of an existing model with `_inherit`. | Yes | Yes | Yes |
| `odoo abstract model` | An abstract model (a mixin with no table). | Yes | Yes | Yes |
| `odoo transient model` | A wizard model. On Odoo 20 it includes a confirm button method. | Yes | Yes | Yes |
| `odoo sql constraints` | `_sql_constraints` | Yes | | |
| `odoo constraint` | `models.Constraint`, which replaces `_sql_constraints`. | | Yes | Yes |
| `odoo index` | A database index with `models.Index`. | | | Yes |
| `odoo unique index` | A unique index, optionally partial, with an error message (`models.UniqueIndex`). | | | Yes |
| `odoo controller` | An HTTP page route and a `jsonrpc` route. | | | Yes |

## Fields

Type `odoo` and the field name, for example `odoo many2one field`. Each snippet has the common arguments filled in.

| Snippet | Inserts |
| :--- | :--- |
| `odoo boolean field` | `fields.Boolean(string="Name", default=False)` |
| `odoo char field` | `fields.Char(string="Name", required=False, translate=False)` |
| `odoo text field` | `fields.Text(string="Name", translate=False)` |
| `odoo html field` | `fields.Html(string="Name", sanitize=True, translate=False)` |
| `odoo integer field` | `fields.Integer(string="Name", default=0)` |
| `odoo float field` | `fields.Float(string="Name", digits=(16, 2))` |
| `odoo monetary field` | `fields.Monetary(string="Name", currency_field="currency_id")` |
| `odoo date field` | `fields.Date(string="Name", default=fields.Date.context_today)` |
| `odoo datetime field` | `fields.Datetime(string="Name", default=fields.Datetime.now)` |
| `odoo selection field` | `fields.Selection` with three example choices and a default. |
| `odoo many2one field` | `fields.Many2one('res.partner', string="Name", ondelete='set null')`. Choose `set null`, `restrict` or `cascade` from the list. |
| `odoo one2many field` | `fields.One2many('model.name', 'inverse_field_id', string="Lines")` |
| `odoo many2many field` | `fields.Many2many('res.partner.category', string="Tags")` |
| `odoo binary field` | `fields.Binary(string="File", attachment=True)` |
| `odoo image field` | `fields.Image(string="Image", max_width=1024, max_height=1024)` |
| `odoo reference field` | `fields.Reference` with a list of models to choose from. |
| `odoo json field` | `fields.Json(string="Data")` |
| `odoo many2onereference field` | `fields.Many2oneReference` with a `model_field`. Odoo 20 only. |

The values above are for Odoo 20. On Odoo 18 and 19, the snippets also add a `help` argument and, for most fields, `tracking=True`.

## Methods

| Snippet | What it inserts | Odoo 18 | Odoo 19 | Odoo 20 |
| :--- | :--- | :-: | :-: | :-: |
| `odoo create method` | A `create` override that takes a list of value dictionaries (`@api.model_create_multi`). | Yes | Yes | Yes |
| `odoo write method` | A `write` override. | Yes | Yes | Yes |
| `odoo unlink method` | An `unlink` override. | Yes | Yes | Yes |
| `odoo onchange method` | An `@api.onchange` method. | Yes | Yes | Yes |
| `odoo compute method` | A computed field and its `@api.depends` method. | Yes | Yes | Yes |
| `odoo constraints method` | An `@api.constrains` method that raises a `ValidationError`. | Yes | Yes | Yes |
| `odoo default get method` | A `default_get` override. | Yes | Yes | Yes |
| `odoo action method` | An `action_` method that returns a window action, for example for a smart button. | Yes | Yes | Yes |
| `odoo ondelete method` | An `@api.ondelete` method that blocks deletion under a condition. | | | Yes |
| `odoo search name method` | A `_search_display_name` override, so name search also matches another field. | | | Yes |
| `odoo display name method` | A `_compute_display_name` method that changes how records are labelled. | | | Yes |

Typing `@api.` also offers each decorator as a ready-made method, for example `@api.depends` with a method below it.

## Errors, notifications and actions

These are the same on Odoo 18, 19 and 20.

| Snippet | What it inserts |
| :--- | :--- |
| `odoo validation error` | `raise ValidationError(...)` |
| `odoo user error` | `raise UserError(...)` |
| `odoo access error` | `raise AccessError(...)` |
| `odoo missing error` | `raise MissingError(...)` |
| `odoo redirect warning` | `raise RedirectWarning(...)` with an action ID and a button label. |
| `odoo notification` | A `display_notification` client action. Choose `success`, `info`, `warning` or `danger`. |
| `odoo rainbow man` | The rainbow man effect. Choose the fade-out speed. |
| `odoo return action` | A window action that opens a record, in the same view or in a dialog. |
| `odoo url action` | An `ir.actions.act_url` action that opens a URL. |

Each error snippet needs its import, for example `from odoo.exceptions import ValidationError`. Use the import snippets below.

## Imports

| Snippet | Inserts |
| :--- | :--- |
| `odoo common imports` or `odoo import common` | `from odoo import api, fields, models` |
| `odoo import api`, `odoo import fields`, `odoo import models` | The single import. |
| `odoo import exceptions` | `from odoo.exceptions import UserError, ValidationError` |
| `odoo import http` | `from odoo import http` |
| `odoo import http request` | `from odoo.http import request` |
| `odoo import logging` | `import logging` and `_logger = logging.getLogger(__name__)` |
| `odoo import tools`, `odoo import config`, `odoo import date_utils` | Imports from `odoo` and `odoo.tools`. |
| `odoo import json`, `odoo import base64`, `odoo import re`, `odoo import datetime`, `odoo import html` | Python standard library imports. |
| `odoo import werkzeug.utils` | `from werkzeug.utils import redirect` |

## Examples

These are Odoo 20 outputs.

```python
# odoo new model class
from odoo import api, fields, models

class ModelName(models.Model):
    _name = 'model.name'
    _description = 'Model Description'
    _order = 'name'

    name = fields.Char(string='Name', required=True)
    active = fields.Boolean(default=True)

    _name_uniq = models.Constraint('unique(name)', 'The name must be unique.')
```

```python
# odoo compute method
field_name = fields.Char(string='Field Label', compute='_compute_field_name', store=True)

@api.depends('dependency_field')
def _compute_field_name(self):
    for record in self:
        record.field_name = value
```

```python
# odoo ondelete method
@api.ondelete(at_uninstall=False)
def _unlink_except_done(self):
    for record in self:
        if record.state == 'done':
            raise UserError(self.env._("You cannot delete this record."))
```

```python
# odoo notification
return {
    'type': 'ir.actions.client',
    'tag': 'display_notification',
    'params': {
        'title': self.env._("Title"),
        'message': self.env._("Message"),
        'type': 'success',
        'sticky': False,
    },
}
```

```python
# odoo controller
from odoo import http
from odoo.http import request

class MyController(http.Controller):

    @http.route('/my_module/page', type='http', auth='user')
    def page(self, **kwargs):
        return request.render('my_module.template_id', {})

    @http.route('/my_module/data', type='jsonrpc', auth='user')
    def data(self, **kwargs):
        return {'records': request.env['res.partner'].search_read([], ['name'], limit=10)}
```

## Tips and limits

* Snippets use the Odoo version of the project. If a snippet you expect is missing, check the version in the status bar. For example, `odoo constraint` does not appear on Odoo 18.
* Placeholders that appear more than once, such as a field name in a compute method, change together as you type.

## Next steps

* Use [XML snippets](/assista-ide/snippets/xml-snippets) for views and data.
* Get [context-aware suggestions](/assista-ide/odoo-standards/code-suggestion) as you type.
* [Create modules and files](/assista-ide/module-creation).
