---
url: https://docs.cybrosys.com/pycharm-plugin/features/code-inspection.md
description: >-
  Inspect Odoo Python, XML and CSV files in PyCharm for naming, structure and
  security conventions, with quick fixes.
---

# Code inspection

Get inline warnings in Python, XML and CSV files when your code departs from Odoo conventions. Each warning shows the exact location and the value the plugin expects, and some offer a quick fix. Inspections run as you edit.

## What you can do

| File type | What is checked |
|---|---|
| **Python** | File structure, manifest keys and hooks, model and class names, field names and attributes, function names and decorators. |
| **XML** | Record and field order, view, menu, action, group and rule names and IDs, and the links between menus and actions. |
| **CSV** | Structure and naming of `ir.model.access.csv`. |

### Python

**File structure**

* The file starts with the UTF-8 declaration `# -*- coding: utf-8 -*-`.
* Imports in `__init__.py` are in alphabetical order, and so are the items in `from odoo import ...`.

```python
# -*- coding: utf-8 -*-
from . import models
from . import wizards

from odoo import api, fields, models
```

**Manifest (`__manifest__.py`)**

* Required keys are present: `name`, `summary`, `description`, `version`, `license`, `category` and `images`.
* `summary` and `description` are different, and `images` is not empty.
* `version` uses five numbers, such as `16.0.1.0.0`.
* If you declare a hook (`pre_init_hook`, `post_init_hook`, `uninstall_hook` or `post_load`), the matching function exists in `hooks.py`.

**Models and classes**

* The file name matches the model: `sale.order` is in `sale_order.py`.
* The class name is PascalCase: `SaleOrder`.
* `_name` uses only lowercase letters, dots and underscores.
* `_description` is set when `_name` is set.

**Fields**

| Rule | Example |
|---|---|
| `Many2one` names end with `_id`. | `partner_id = fields.Many2one('res.partner')` |
| `One2many` and `Many2many` names end with `_ids`. | `line_ids = fields.One2many('sale.order.line', 'order_id')` |
| Do not set default values explicitly. | Use `fields.Char()`, not `fields.Char(required=False)`. The same applies to `readonly=False` and `store=True` on stored fields. |
| No conflicting attributes. | `fields.Char(required=True, readonly=True)` is flagged. |
| `string` starts with a capital letter. | `string='Customer Name'` |
| `string` is not redundant. | `name = fields.Char(string='Name')` is flagged because Odoo derives it. |
| `string` and `help` differ. | `string='Description', help='Description'` is flagged. |

The redundant-attribute warnings have a quick fix that removes the attribute.

**Function names**

| Kind | Pattern | Example |
|---|---|---|
| `@api.onchange` | `_onchange_<field_name>` | `_onchange_partner_id` |
| `@api.constrains` | `_check_<field_name>` | `_check_amount_total` |
| Compute method | `_compute_<field_name>` | `_compute_amount_total` |
| Default method | `_default_<field_name>`, with `@api.model` | `_default_currency_id` |

Call a default method from the field with a lambda:

```python
currency_id = fields.Many2one(
    'res.currency',
    default=lambda self: self._default_currency_id()
)

@api.model
def _default_currency_id(self):
    return self.env.company.currency_id
```

### XML

**Records and fields**

* A `<record>` has `id` and `model` attributes, with `id` first.
* A `<field>` has `name` as its first attribute.
* Non-updatable data sits in `<data noupdate="1">`.

**Names and IDs**

The plugin derives the model name from the file name and the view type from the `arch`, then checks the pattern.

| Element | Pattern | Example |
|---|---|---|
| View name | `<model.name>.view.<type>` | `sale.order.view.form` |
| View ID | `<model_name>_view_<type>` | `sale_order_view_form` |
| Menu ID | `<model_name>_menu_<description>` | `sale_order_menu_main` |
| Action ID | `<model_name>_action` | `sale_order_action` |
| Record rule ID | `<model_name>_rule_<group_type>` | `sale_order_rule_company` |
| Group ID | `<module_name>_group_<group_name>` | `sale_management_group_manager` |

* A rule counts as a `company` rule when its domain contains `company_id`, and as a `user` rule when it uses `base.group_user`.
* The module name comes from the file path.
* Menu IDs must match `^[a-z0-9]+(_[a-z0-9]+)*_menu(_[a-z0-9]+)*$`.

**Cross-checks**

The plugin tracks the links between menu items and their actions, and checks them for consistency.

```xml
<record id="sale_order_view_form" model="ir.ui.view">
    <field name="name">sale.order.view.form</field>
    <field name="model">sale.order</field>
    <field name="arch" type="xml">
        <form>
            <field name="name"/>
        </form>
    </field>
</record>

<menuitem id="sale_order_menu_main"
          name="Sales Orders"
          action="sale_order_action"
          parent="sale.sale_menu_root"/>
```

### CSV

For `ir.model.access.csv`:

* The file has a header and at least one data line.
* The columns `id`, `name` and `model_id` are present.
* Access IDs follow `access_<model_name>` and names follow `access.<model.name>`, derived from the `model_id` value.
* Quoted fields with commas and escaped quotes are read correctly, and a warning points at the exact field.

| `model_id` | Expected `id` | Expected `name` |
|---|---|---|
| `model_sale_order` | `access_sale_order` | `access.sale.order` |
| `model_account_move` | `access_account_move` | `access.account.move` |
| `model_res_partner` | `access_res_partner` | `access.res.partner` |

```csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order,access.sale.order,model_sale_order,base.group_user,1,1,1,1
access_account_move,access.account.move,model_account_move,base.group_user,1,0,0,0
```

## Get started

Inspections run automatically as you edit. To turn them on or off:

1. Open **Settings** > **Editor** > **Inspections** > **Cybrosys Assista**.
2. Select or clear the inspections you want:

| Inspection | Controls |
|---|---|
| **Assista Py-Inspection** | Python standard inspections |
| **Assista XML-Inspection** | XML standard inspections |
| **Assista CSV-Inspection** | CSV standard inspections |

## Tips and limits

* Enable all three inspections from the start, and fix warnings as they appear rather than at review time.
* Share your inspection settings with your team so everyone sees the same warnings.
* Guidelines change between Odoo releases. Check the current [Odoo coding guidelines](https://www.odoo.com/documentation/master/contributing/development/coding_guidelines.html) if a warning looks wrong for your version.

## Next steps

* [Smart suggestions](/pycharm-plugin/features/smart-suggestions)
* [Quick start](/pycharm-plugin/quick-start)
