# ir.model.access.csv (Odoo glossary)

Canonical: https://erpfly.com/glossary/ir-model-access-csv/
Last updated: September 14, 2026

**Definition:** ir.model.access.csv is the CSV data file in an Odoo module's security folder that creates ir.model.access records, each granting one user group read, write, create or delete rights on a whole model. A model with no line in it can only be used by superuser code.

Also called: ir_model_access.csv, Odoo access rights CSV, ir.model.access

You add a new model to an Odoo addon, install it, open the menu as a normal user and get an access error. The usual cause is a missing line in this file. Odoo does tell you, but only in the server log, where it's easy to miss:

```
WARNING mydb odoo.modules.loading: The models ['equipment.rental'] have no access rules in module equipment_rental, consider adding some, like:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
```

Access is checked against `ir.model.access` records, and the only thing that skips that check is superuser mode (`sudo()` or the internal superuser account). The Administrator user doesn't get a pass. No line means nobody else gets in.

### Reading a line

Here's the file for a small rental module with a user group and a manager group, plus a return wizard:

```
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_equipment_rental_user,equipment.rental user,model_equipment_rental,group_rental_user,1,1,1,0
access_equipment_rental_manager,equipment.rental manager,model_equipment_rental,group_rental_manager,1,1,1,1
access_rental_return_wizard_user,rental.return.wizard user,model_rental_return_wizard,group_rental_user,1,1,1,1
```

- **id** is the external ID of the access record. `access_<model>_<group>` is the usual convention.
- **name** is a label you'll see in the access rights list in developer mode.
- **model_id:id** points at the model's own external ID, which Odoo builds as `model_` plus the `_name` with dots swapped for underscores. For a model from another module, prefix it: `sale.model_sale_order`.
- **group_id:id** is the group's external ID. Leave it empty and the line applies to every user, portal and public users included.
- **perm_read, perm_write, perm_create, perm_unlink** take 1 or 0. Unlink means delete.

The file has to be listed in the `data` key of the manifest, after the XML file that defines the groups it references. Data files load in order, so a group that doesn't exist yet makes the install fail.

### How rights from several groups combine

Access rights are additive. A user gets the union of every line that matches any of their groups. If one group grants read and create and another grants write, the user can do all three.

That has a consequence people don't expect: a line full of zeros takes nothing away. You can't use this file to deny. If a user shouldn't delete bookings, none of their groups may grant `perm_unlink` on that model.

It also only works at model level. "Salespeople can see their own bookings but not their colleagues'" is a job for record rules, which filter individual records after the access line lets the user in.

### Mistakes we see in review

**Forgetting the wizard.** Transient models count. The loading check covers every model that isn't abstract, so a `TransientModel` behind a popup needs its own line, or the button that opens it fails for everyone but the superuser.

**Adding lines for inherited models.** If you only `_inherit = "sale.order"` to add fields, the sale module already ships the access lines. Because rights add up, an extra line there can quietly widen who may edit or delete orders. You need lines for new `_name` models only.

**An empty group on business data.** It looks tidy and it opens the model to portal users. Use `base.group_user` at the very least, and preferably your module's own groups.

**Everything set to 1 for `base.group_user`.** It makes the warning go away, and it's what a lot of tutorials show. On a real database it means any employee can delete any record of that model.

When erpfly generates an addon, every new model gets access lines scoped to the module's groups, and the CSV is loaded after the security XML. Our [guide to building an Odoo module](https://erpfly.com/blog/create-custom-module-odoo/) shows where the file sits alongside models and views, and [Odoo module development](https://erpfly.com/odoo-module-development/) covers what we generate for Odoo 17, 18 and 19.

See also: https://erpfly.com/glossary/record-rules/, https://erpfly.com/glossary/odoo-addon/, https://erpfly.com/glossary/odoo-manifest/

### Sources

- [Security in Odoo: Access Rights, Odoo 19 documentation](https://www.odoo.com/documentation/19.0/developer/reference/backend/security.html#access-rights)
- [Chapter 4: Security, A Brief Introduction, Odoo 19 documentation](https://www.odoo.com/documentation/19.0/developer/tutorials/server_framework_101/04_securityintro.html)
- [odoo/modules/loading.py (19.0), odoo/odoo on GitHub](https://github.com/odoo/odoo/blob/19.0/odoo/modules/loading.py)
- [odoo/addons/base/models/ir_model.py (19.0), odoo/odoo on GitHub](https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/ir_model.py)