Skip to content

The model options

A model is declared with its fields, and the rest is a list of options. The tour covers the main ones as it goes. Eight are named nowhere, although they change visibility, navigation or the screen.

read_open: reference data

A product, a partner, a unit, a currency are read by roles from every module. Listing each role in the read permission is unmaintainable and breaks the moment a module arrives (rule 8).

registry.register_model("library_title", module="library", read_open=True, fields=[...])

Run with a user who belongs to no group at all:

without read_open: with read_open=True:
GET /library_title -> 200 [] GET /library_title -> 200 [Dune, ...]
GET /library_title/1 -> 404 GET /library_title/1 -> 200
POST /library_title -> 403 POST /library_title -> 403

Two things to take from that table:

  • writing does not move. read_open opens reading and nothing else: creating is still refused by the model’s permission;
  • without read permission a list answers 200 and empty, not 403. A single record answers 404, not 403. The kernel filters instead of refusing, and never tells someone a record exists when they may not see it. A module expecting a 403 on the list expects the wrong thing.

14 of the product’s modules use this option.

By default any group that can read a model sees it in the navigation. That is not always wanted: a technical model must stay readable (otherwise the dropdowns pointing at it break) without cluttering everybody’s menu.

registry.register_model("library_shelfmark", module="library", menu_groups=("library_librarian",), ...)

The navigation shows it to librarians only; permissions do not change. It is screen tidying, never security. 12 modules use it.

ui_managed: records you do not create by hand

Some records are born from a controlled flow (a state transition’s effect, a generated entry) and never from a “New” button. With ui_managed=True the interface drops the create / edit / delete affordances, the list stays visible as history, and the real permissions do not change: server-side writes still go through.

settings: configuration, not data

A model marked settings=True is placed by the frontend under Settings instead of the module’s business navigation. Purely navigational: permissions, rules and storage are untouched.

create_route: your own create screen

create_route="/forms/new/edit"

The generic “New” no longer opens: it redirects to your screen. A form built by assembling blocks has no business inside a generic field form. Everything else (permissions, API) is unchanged: it is a discovery option, not a back door.

image_field and image_fallback: the thumbnail

image_field names the file field used as a thumbnail, and that alone makes the gallery view appear, with no configuration. image_fallback takes over when the record has no image of its own:

image_field="photo", image_fallback=("user_id", "avatar_file")

An employee with no photo shows their user’s avatar. One relation, one field, and no if in the interface.

path_field: hierarchies

Declares which field stores the parent-chain path, and how to build it. The session recomputes it for a moved record and all its descendants, so it never drifts. Rendered read-only in views: it is not something you type.

per_website: several storefronts in one tenant

Adds a website_id column and partitions records per public site. No business module uses it today: it is a kernel option for multi-site storefronts, named here so it stops being invisible.