Widgets
Widgets control how fields are displayed and edited.
Default Widget Mapping
Section titled “Default Widget Mapping”When no explicit widget is specified in a view, the system automatically selects a default widget based on the field type:
| Field Type | Default Widget |
|---|---|
| Char | TextInput |
| Text | TextArea |
| Integer | NumberInput |
| Float | NumberInput |
| Monetary | Monetary |
| Boolean | Checkbox |
| Date | DatePickerInput |
| Datetime | DateTimePicker |
| Selection | SelectCombo |
| ManyToOne | DataCombo |
| OneToOne | DataCombo |
| ManyToMany | MultiCombo |
| OneToMany | List |
| File | FileInput |
| JSON | JSONInput |
You can override the default by specifying a widget property:
- type: field name: status properties: widget: BadgeUser relations always render with an avatar
Section titled “User relations always render with an avatar”A relation to the User model (a ManyToOne/OneToOne, e.g. assigned_to, manager, salesperson) should show a face, not just a name. So it is resolved to the avatar widget variant automatically — AvatarCombo on Form and List views, Avatar on Kanban cards — and its avatarField defaults to the user’s image. You do not need to set widget or avatarField on a user field; it renders as an avatar with no configuration.
This applies whether the field specifies no widget or an explicit DataCombo/Text — a plain user picker is never the intent, so the avatar wins. A user with no image falls back to auto-coloured initials.
Opt out on the rare field where a plain text picker is genuinely wanted (e.g. a very dense grid) with avatar: false:
- type: field name: assigned_to properties: avatar: false # keep the plain DataCombo, no avatarNotes:
- Only single-user relations are upgraded. A
ManyToManyto User keepsMultiCombo(there is no avatar multi-select widget). - An explicit avatar widget or any other deliberate widget choice is left untouched.
- This is scoped to the
Usermodel only.
Widget Mode Support
Section titled “Widget Mode Support”Widgets support different view modes (Form, List, FormList, Kanban, Display). When a widget is used in an unsupported mode, it falls back to display mode if available, or shows “Unsupported widget”.
| Widget | Form | List | FormList | Kanban | Display | Notes |
|---|---|---|---|---|---|---|
| Dashboard Widgets | ||||||
| Card | ✓ | - | - | - | ✓ | KPI/metric display |
| CompactList | ✓ | - | - | ✓ | ✓ | Summary table/list |
| Display Widgets | ||||||
| LinkedRecords | ✓ | - | - | - | ✓ | Related records with actions |
| Avatar | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Badge | ✓ | ✓ | ✓ | ✓ | ✓ | |
| BarChart | ✓ | - | - | ✓ | ✓ | Not suitable for inline table cells |
| AreaChart | ✓ | - | - | ✓ | ✓ | Not suitable for inline table cells |
| Sparkline | ✓ | ✓ | ✓ | ✓ | ✓ | Compact, suitable for inline |
| Image | ✓ | ✓ | ✓ | ✓ | ✓ | |
| ProgressBar | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Rating | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Url | ✓ | ✓ | ✓ | ✓ | ✓ | Anchor + copy button; read-only |
| StatusBar | ✓ | - | - | - | - | Top-level form element type: statusbar, not a field widget |
| Input Widgets | ||||||
| TextInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| TextArea | ✓ | ✓ | ✓ | ✓ | ✓ | |
| NumberInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Monetary | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Checkbox | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Switch | ✓ | ✓ | ✓ | ✓ | ✓ | |
| DatePickerInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| DateTimePicker | ✓ | ✓ | ✓ | ✓ | ✓ | |
| DateRangePicker | ✓ | - | - | - | ✓ | Form + Display only |
| TimeInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| ColorInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| RangeSlider | ✓ | - | - | ✓ | ✓ | Not suitable for inline editing |
| Slider | ✓ | - | - | ✓ | ✓ | Not suitable for inline editing |
| RichTextEditor | ✓ | - | - | - | ✓ | Form + Display only |
| FileInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Dropzone | ✓ | ✓ | ✓ | ✓ | ✓ | Drag-and-drop single file upload |
| PdfViewer | ✓ | - | - | - | ✓ | Inline PDF preview of a File field |
| RadioCards | ✓ | ✓ | ✓ | ✓ | ✓ | |
| PasswordInput | ✓ | - | - | - | - | Form-only by design |
| JSONInput | ✓ | ✓ | ✓ | ✓ | ✓ | |
| FormulaInput | ✓ | ✓ | ✓ | ✓ | ✓ | Sandboxed formula editor with ƒx palette |
| MultiFileUpload | ✓ | ✓ | ✓ | ✓ | ✓ | Multiple file attachments |
| CodeEditor | ✓ | ✓ | ✓ | ✓ | ✓ | Syntax-highlighted code (CodeMirror) |
| Signature | ✓ | ✓ | ✓ | ✓ | ✓ | Canvas signature; stored as PNG data URL |
| Select Widgets | ||||||
| SelectCombo | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Radio | ✓ | ✓ | ✓ | ✓ | ✓ | |
| DataCombo | ✓ | ✓ | ✓ | ✓ | ✓ | |
| AvatarCombo | ✓ | ✓ | ✓ | ✓ | ✓ | DataCombo with avatar display |
| MultiCombo | ✓ | ✓ | ✓ | ✓ | ✓ | |
| GroupAccessManager | ✓ | - | - | ✓ | ✓ | Complex UI, form-only editing |
| MultiOptionGroup | ✓ | - | - | ✓ | ✓ | Complex UI, form-only editing |
| OptionGroups | ✓ | - | - | - | ✓ | Renders a OneToMany as one labelled option group per row instead of a grid — see below |
| Layout Widgets | ||||||
| List | ✓ | - | - | - | ✓ | Embedded editable list (FormList) |
| Tags | ✓ | ✓ | - | ✓ | ✓ | |
| Title | ✓ | ✓ | ✓ | ✓ | ✓ | Section headers |
| Divider | ✓ | - | - | - | ✓ | Visual separator |
| Button | ✓ | - | - | ✓ | - | Action button |
| LinkButton | ✓ | ✓ | ✓ | ✓ | ✓ | Element type: linkButton, not a field widget |
| Additional Input Widgets | ||||||
| PinInput | ✓ | - | - | - | - | Form-only, numeric PIN entry |
| SegmentedControl | ✓ | ✓ | ✓ | ✓ | ✓ | Button group selection |
| MonthPickerInput | ✓ | ✓ | ✓ | ✓ | ✓ | Month selector |
| YearPickerInput | ✓ | ✓ | ✓ | ✓ | ✓ | Year selector |
| ObjectFieldsInput | ✓ | - | - | - | ✓ | JSON object editor |
| FilterBuilder | ✓ | - | - | - | ✓ | Query filter builder |
| Additional Display Widgets | ||||||
| Icon | ✓ | - | - | ✓ | ✓ | Lucide icon display |
| BoxedIcon | ✓ | - | - | ✓ | ✓ | Icon with background |
| WebsitePreview | ✓ | - | - | ✓ | ✓ | Website iframe preview |
| SummaryIndicator | ✓ | ✓ | ✓ | ✓ | ✓ | View-icon indicator; click opens a detail popover |
Legend:
- ✓ = Supported
- - = Not supported (falls back to display mode or shows “Unsupported widget”)
Input Widgets
Section titled “Input Widgets”TextInput
Section titled “TextInput”Single-line text input.
- type: field name: name properties: widget: TextInput placeholder: Enter name size: lg| Property | Description |
|---|---|
placeholder | Placeholder text |
size | xs, sm, md, lg, xl |
disabled | Disable input |
TextArea
Section titled “TextArea”Multi-line text input.
- type: field name: description properties: widget: TextArea rows: 4 placeholder: Enter descriptionRichTextEditor
Section titled “RichTextEditor”WYSIWYG HTML editor: headings, bold/italic/underline, links, bullet/numbered/check lists, inline images, and tables.
- type: field name: content properties: widget: RichTextEditorWhat survives a round-trip. The editor stores a document, not a string of markup: HTML
you write into the field is parsed into that document and re-serialized when it is read
back, and anything the document model has no place for is dropped — only its text is kept.
The elements listed above round-trip, and so does a style attribute on a table, row or
cell (which is how a table keeps its look once it is mailed, since mail clients drop
<style> blocks). Markup outside that set — a <div> wrapper, a <section>, a styled
<span> — is not preserved. If a field holds HTML you need back byte-for-byte, store and
display it rather than putting it behind an editor.
CodeEditor
Section titled “CodeEditor”Syntax-highlighted code editor (CodeMirror 6) for script/markup fields. Select the
language with the language prop — not mode (mode is the view render mode).
- type: field name: custom_js properties: widget: CodeEditor language: javascript| Property | Description |
|---|---|
language | json, javascript, python, markdown, or html (defaults to plain) |
minHeight | Editor min height (e.g. "160px") |
Signature
Section titled “Signature”Canvas-based signature capture (no external dependency). The drawn signature is
stored on the field as a PNG data URL string; read-only and display modes render it
as an image. Back it with a Text/Char field.
- type: field name: signature properties: widget: Signature label: Customer SignatureNumberInput
Section titled “NumberInput”Numeric input with optional formatting.
- type: field name: price properties: widget: NumberInput precision: 2 prefix: $ min: 0| Property | Description |
|---|---|
precision | Decimal places |
prefix | Static text before value (e.g., "$", "€") |
suffix | Static text after value (e.g., "%", "kg") |
suffix_field | false only — drop the unit the model’s suffix_field prints, because this view shows that unit field itself |
min | Minimum value |
max | Maximum value |
:::tip Dynamic Prefix/Suffix
For dynamic values from related fields (e.g., displaying UOM name), define prefix_field or suffix_field on the model field definition instead:
# Model definitionquantity = Float(precision=3, suffix_field="uom__name")The model’s dynamic fields take priority over the view’s static values. See Prefix and Suffix Fields.
Where the view renders that unit itself — a UoM column next to the quantity column, a UoM
picker next to the input — set suffix_field: false on the number so the unit isn’t
printed twice (5.00 Units | Units). Only false is accepted; it turns the model’s unit
off for that field in that view and nothing else.
:::
Monetary
Section titled “Monetary”The money widget — the default for Monetary fields and the right choice whenever a
value is currency. It formats the amount with the record’s currency symbol, position and
rounding, and is locale-aware. Bind the currency with currency_field (the model field
holding the currency object); in dashboard/display widgets the alias currencyField is
also accepted.
- type: field name: amount properties: widget: Monetary currency_field: currency| Property | Description |
|---|---|
currency_field | Sibling field holding the currency (symbol/position/rounding). Prefer setting currency_field on the model field so it applies everywhere. |
precision | Override decimal places (otherwise from the currency’s rounding) |
:::tip Model-side currency
For form fields bound to a model column, set currency_field on the field definition
itself rather than repeating it in every view. See
Prefix and Suffix Fields.
:::
JSONInput
Section titled “JSONInput”Raw JSON editor — the default widget for JSON fields. It pretty-prints the value,
validates on blur, and saves the parsed object (not the string). Display and kanban
modes render the JSON read-only in a code block.
- type: field name: procurement_trace properties: widget: JSONInput readonly: true visible: Q(procurement_trace__isnotnull=True)| Property | Description |
|---|---|
minRows | Minimum textarea rows in form mode (default: 4) |
maxRows | Maximum textarea rows before scrolling (default: 12) |
placeholder | Placeholder text |
For an object editor with typed key/value rows instead of raw text, see
ObjectFieldsInput.
FormulaInput
Section titled “FormulaInput”A single-line editor for safe-eval formula fields (e.g. salary components, financial
report lines). It surfaces the expression vocabulary through a ƒx palette listing
the available variables and functions, and validates client-side (no string literals,
only known names) so mistakes surface before save.
The variable namespace comes from one of two sources:
# Server namespace: a no-arg classmethod returning {variables, functions}- type: field name: formula properties: widget: FormulaInput label: Formula namespaceModel: Payslip namespaceMethod: formula_namespace# Sibling-row namespace: variables are the live values of a peer grid column- type: field name: formula properties: widget: FormulaInput label: Formula placeholder: e.g. revenue - cogs variablesFromField: code # peer column supplying variable names variableLabelField: label # peer column used as each variable's description| Property | Description |
|---|---|
namespaceModel | Model whose formula_namespace() supplies variables/functions (default Payslip) |
namespaceMethod | Classmethod name returning {variables, functions} (default formula_namespace) |
variablesFromField | Source variables from a sibling grid column instead of the server |
variableLabelField | Sibling column used as each variable’s description |
placeholder | Placeholder formula |
MultiFileUpload
Section titled “MultiFileUpload”Upload and manage multiple file attachments on one field. Form mode shows the uploader; list/kanban show a count badge; display shows the file-name badges.
- type: field name: attachments properties: widget: MultiFileUpload label: Attachments| Property | Description |
|---|---|
accept | Accepted MIME types / extensions (e.g. "image/*,.pdf") |
For a single file, use FileInput/ImageInput.
Dropzone
Section titled “Dropzone”Single-file upload with a drag-and-drop surface — the visual alternative to
FileInput’s bordered “Select File” button. Drop a file onto the area, or click
to browse. Same value contract as FileInput (back it with a File field), so
the two are interchangeable; use Dropzone where the upload deserves a prominent
target and FileInput for a compact control. Outside form mode it renders the
same compact filename cell as FileInput.
- type: field name: document properties: widget: Dropzone label: PDF placeholder: Drag a PDF here or click to upload| Property | Description |
|---|---|
placeholder | Prompt shown inside the drop area |
accept | Accepted MIME types (e.g. "application/pdf") |
maxSize | Max file size in bytes |
PdfViewer
Section titled “PdfViewer”Inline preview of a PDF held in a File field — renders the file’s served
URL in an <iframe> (browsers render PDFs natively; no extra dependency).
Read-only; pair it with Dropzone/FileInput for upload. By default in a form it
sticks to the top of its column so the document stays in view while the surrounding
content scrolls — a document-centric layout (preview beside the fields/tabs). When
the viewer lives in its own scroll column — e.g. a right-docked
propertiesPanel beside the
canvas — set sticky: false, since the pane already scrolls independently.
- type: field name: document properties: widget: PdfViewer height: 75vh nolabel: true| Property | Description |
|---|---|
height | Viewer height (e.g. "75vh", 640) — defaults to 75vh |
sticky | Stick to the top while surrounding content scrolls — defaults to true in a form; set false when the viewer is in its own independently-scrolling pane |
DatePickerInput
Section titled “DatePickerInput”Date selector.
- type: field name: due_date properties: widget: DatePickerInputDateTimePicker
Section titled “DateTimePicker”Date and time selector.
- type: field name: scheduled_at properties: widget: DateTimePickerSwitch
Section titled “Switch”Boolean toggle.
- type: field name: active properties: widget: Switch label: ActiveCheckbox
Section titled “Checkbox”Boolean checkbox.
- type: field name: terms_accepted properties: widget: Checkbox label: I accept the termsRadio button group for Selection fields and ManyToOne fields.
Selection field example:
- type: field name: priority properties: widget: Radio orientation: horizontalManyToOne field example:
- type: field name: category properties: widget: Radio orientation: vertical filter: Q(active__eq=true)| Property | Description |
|---|---|
orientation | horizontal (default) or vertical |
size | xs (default), sm, md, lg |
filter | Q-expression to filter M2O options |
color | Mantine color for radio buttons |
For ManyToOne fields, all records from the related model are fetched and displayed as radio options.
RadioCards
Section titled “RadioCards”Card-based radio selection with icon, label, and description. Supports both Selection fields and ManyToOne fields. Ideal for visually rich option selection.
Selection field with custom options:
- type: field name: type properties: widget: RadioCards columns: 3 choices: - value: Consumable label: Consumable icon: Package description: Used once, not tracked in inventory - value: Stockable label: Stockable icon: Warehouse description: Tracked in inventory with stock levels - value: Service label: Service icon: Wrench description: Non-physical service offeringManyToOne field example:
- type: field name: product_category properties: widget: RadioCards icon: Folder columns: 2 filter: Q(parent__isnull=true)| Property | Description |
|---|---|
choices | Array of option objects with value, label, icon, description |
columns | Number of columns for grid layout |
size | sm, md (default), lg |
icon | Fallback icon for all cards (required for M2O, optional for Selection) |
filter | Q-expression to filter M2O options |
color | Mantine color for badges in display modes |
Option object properties (for Selection fields):
| Property | Required | Description |
|---|---|---|
value | Yes | The value stored when selected |
label | No | Display label (defaults to value) |
icon | No | Lucide icon name (overrides fallback icon prop) |
description | No | Description text below label |
Notes:
- For Selection fields: options can be provided explicitly or derived from the field’s choices
- For ManyToOne fields: all records are fetched from the related model. Use
iconprop to set a fallback icon (defaults toIconCircleif not provided) - Both widgets support
groups,visible, andreadonlyproperties (handled at the form level)
SelectCombo
Section titled “SelectCombo”Selection dropdown for Selection fields.
- type: field name: status properties: widget: SelectComboDataCombo
Section titled “DataCombo”Dropdown for ManyToOne fields.
- type: field name: customer properties: widget: DataCombo filter: Q(is_customer=True)| Property | Description |
|---|---|
filter | Domain filter for options |
create | Allow inline “Create” / “Create & Edit” of new records from the dropdown (default true). Set create: false to force selection from existing records only. Also gated by permission — the options never appear unless the user has create access on the related model. |
edit | Show the open/edit icon that opens the selected record’s form (default true). Set edit: false to hide it (select-only). When shown, the form opens in edit mode only if the user has update access on that record — otherwise it opens read-only. |
context | Default values for “Create & Edit” (see Field-Level Context) |
description_field | Field on the related model shown as a muted second line under the selected value (see below) |
description_field — a second line under the selected value. Point it at a field on
the related model (e.g. a Contact’s address) and the picker shows that value as a muted line
beneath the chosen record — useful to disambiguate look-alike names. It renders for the
selected value only (not in the search dropdown). The referenced field must be a
Selection, Char, Text, or ManyToOne (a relation shows its display name) on the
related model, and is fetched automatically — you don’t list it elsewhere in the view.
- type: field name: contact properties: widget: DataCombo description_field: formatted_addressPermission note: create and edit are view-design toggles, layered on top of the user’s access rights. create: true still hides the create options for a user without create permission on the related model, and the open icon opens read-only for a user without update permission. These checks reuse the same per-model access the rest of the UI uses; the backend independently enforces them on save. The same create/edit behavior applies to MultiCombo (no open icon) and AvatarCombo.
Where a filter comes from
Section titled “Where a filter comes from”A relation picker’s scope has two possible homes, and most belong in the first:
- The field, via
filter=on theManyToOne/OneToOne/ManyToMany— see Relationships → Scoping the picker. View composition stamps it onto every picker bound to that field, so each view inherits it and none has to remember. This is right whenever the constraint is a property of the field: a payroll journal is a Miscellaneous journal on every screen that shows one. - The view node, via the
filter:property below — for the scope that genuinely differs per screen. Onecontactfield offers customers on an invoice and vendors on a bill; only the view knows which.
When both are present, the view’s filter: replaces the field’s — it is not
intersected with it, because the two-scopes case above would AND to an empty dropdown. To
narrow further, write the whole condition. filter: false opts a screen out of the field’s
scope entirely.
Dynamic Field References in Filters
Section titled “Dynamic Field References in Filters”Filters can reference current form/row values using three patterns:
| Pattern | Description | Example |
|---|---|---|
[field] | Bracketed field reference | Q(company__in=[company]) |
[field__nested] | Bracketed nested traversal | Q(currency__eq=[contact__company]) |
_parent.field | Parent form field (in line items) | Q(company__in=[_parent.company]) |
_parent.field__nested | Parent’s nested field | Q(currency__eq=[_parent.contact__company]) |
field__nested | Unbracketed (after =) | Q(currency__eq=contact__company) |
:::note A reference with no value drops its condition Every reference form is substituted with the live value. When that value is absent, the condition has no subject — “accounts of this invoice’s company” before a company is picked — so that condition is dropped and the rest of the filter still applies. If it was the only condition, no filter is sent and the picker offers everything.
An undefined condition stops constraining rather than excluding everything, because the
alternative renders as a dropdown with no options: indistinguishable, to the user, from
“there is no data”, and explained by nothing. A picker narrowing options is an affordance,
never a guarantee — where a pairing must actually hold, enforce it with a Model.constraint
at the write, which can say why.
“Absent” means null or missing, not falsy. An unticked Boolean is a real answer, so
Q(is_kit=kit_only) with kit_only false narrows to non-kits; only a field the record
does not carry at all is treated as blank.
Only the condition holding the reference goes, so the rest survives:
filter: Q(active=True) & Q(company=_parent.company)| Parent company | Rows offered |
|---|---|
| set | active, in that company |
| blank | active |
The same holds under OR — a branch whose reference is empty drops out and the others
still apply:
filter: Q(warehouse=_parent.warehouse) | Q(transfer_type=_parent.transfer_type)| Warehouse | Transfer Type | Rows offered |
|---|---|---|
| set | set | in that warehouse or of that type |
| blank | set | of that type |
| blank | blank | all (no filter) |
A negated condition is dropped rather than inverted: ~Q(x=y) with y empty would
otherwise return as the strictest condition in the filter.
:::
:::warning Depth Limitation Right-side field references are limited to 2 levels of relation traversal due to backend serialization depth. The install/update process validates this and raises an error if exceeded.
| Depth | Example | Valid |
|---|---|---|
| 1 | contact__id | ✓ |
| 2 | contact__company__id | ✓ |
| 3 | contact__company__currency__id | ✗ Error |
Workarounds for deep nesting:
- Add a
related_fieldon the model to surface the value at a shallower level - Let the backend handle the filter (don’t use field references) :::
Example - Filter by parent form’s company (in invoice line items):
- type: field name: account properties: widget: DataCombo filter: Q(type__nin=['Receivable','Payable']) & (Q(companies__in=[_parent.company]) | Q(companies__isnull=True))Example - Unbracketed field reference:
- type: field name: currency properties: widget: DataCombo filter: Q(id__eq=contact__currency)Context-scoped options (server-side)
Section titled “Context-scoped options (server-side)”A filter can only reference form values, not the current user or anything the
server must compute. When a picker’s options depend on who is logged in (e.g. “only
employees I manage”), give the field a static ctx object and act on it in the related
model’s name_search:
- type: field name: employee properties: widget: DataCombo ctx: scope: org_reports # a signal you defineThe field’s ctx is sent with the options request and exposed server-side via
name_search_ctx. Override the related model’s name_search to read it and narrow the
query — the scope is computed from env.user, so it can’t be tampered with client-side:
from fullfinity.engine.context import name_search_ctx
class Employee(Model): async def name_search(cls, term=None, limit=None, filters=None, operator="icontains"): if name_search_ctx.get().get("scope") == "org_reports": domain = await cls.org_reports_domain() # Q built from env.user, or None if domain is not None: filters = (filters & domain) if filters is not None else domain return await super().name_search(term=term, limit=limit, filters=filters, operator=operator)Only fields that declare ctx send one, so other pickers of the same model are
unaffected. Pair this with a matching check on save (a create/write guard using the same
domain) so the dropdown and the enforcement agree.
AvatarCombo
Section titled “AvatarCombo”Dropdown for ManyToOne fields with avatar display. Similar to DataCombo but shows avatars alongside names in the dropdown options and selected value. Ideal for user/contact selection fields.
- type: field name: assigned_to properties: widget: AvatarCombo avatarField: avatar filter: Q(active=True)| Property | Type | Default | Description |
|---|---|---|---|
avatarField | String | "avatar" | Field name on related model containing avatar attachment ID |
avatarSize | String | "sm" | Avatar size (xs, sm, md, lg, xl) |
avatarRadius | String | "xl" | Avatar border radius |
filter | String | - | Domain filter for options (same as DataCombo) |
create | Boolean | false | Allow creating new records |
context | Object | - | Default values for “Create & Edit” |
Mode Behavior:
| Mode | Behavior |
|---|---|
| Form | Full AvatarCombo input with edit/readonly support |
| List | Avatar combo in edit mode, avatar + name display in view mode |
| FormList | Same as List |
| Kanban | Avatar + name display only (no input) |
| Display | Avatar + name with tooltip |
Example - User assignment with avatar:
- type: field name: user_id properties: widget: AvatarCombo avatarField: avatar avatarSize: sm filter: Q(active=True) & Q(groups__name__in=['Sales'])Note: The avatarField should reference an attachment ID field (File type) on the related model. The widget fetches and caches avatar images automatically.
MultiCombo
Section titled “MultiCombo”Multi-select for ManyToMany fields.
- type: field name: tags properties: widget: MultiCombo| Property | Description |
|---|---|
filter | Domain filter for options |
create | Allow creating new records |
context | Default values for “Create & Edit” (see Field-Level Context) |
ImageInput
Section titled “ImageInput”Image upload field.
- type: field name: photo properties: widget: ImageInput| Property | Description |
|---|---|
width / height | Frame size in pixels |
radius | Corner radius (xs–xl or a number) |
initials_field | Sibling field whose value seeds an auto-coloured initials avatar shown in place of the empty-image placeholder (see below) |
initials_field — initials avatar for empty images. When set, an empty image
renders an auto-coloured initials avatar derived from another field on the record instead
of the generic placeholder icon. Point it at a Selection, Char, or Text field (the
value is used verbatim) or a ManyToOne (the related record’s display name is used); any
other field type is rejected at save time. The referenced field is fetched automatically —
it doesn’t need to appear elsewhere in the view.
- type: field name: image properties: widget: ImageInput width: 140 height: 140 initials_field: nameColorInput
Section titled “ColorInput”Color picker.
- type: field name: color properties: widget: ColorInputSegmentedControl
Section titled “SegmentedControl”Button group for mutually exclusive options. Best for a short Selection (2–4
choices) rendered as an inline toggle. Segments are derived from the field’s
Selection choices automatically — no options needed — or supplied explicitly.
- type: field name: view_mode properties: widget: SegmentedControl fullWidth: true| Property | Description |
|---|---|
choices | Explicit [{ "value": ..., "label": ... }]; omit to derive from the field’s choices |
fullWidth | Expand to fill container width |
orientation | horizontal (default) or vertical |
size | xs, sm, md, lg, xl |
color | Mantine color for active segment |
Like Radio/RadioCards/SelectCombo, options are derived from the field’s
choices unless provided explicitly. Displayed labels are localized; the stored
value stays canonical.
MonthPickerInput
Section titled “MonthPickerInput”Month and year selector.
- type: field name: billing_month properties: widget: MonthPickerInputYearPickerInput
Section titled “YearPickerInput”Year selector.
- type: field name: fiscal_year properties: widget: YearPickerInputPinInput
Section titled “PinInput”Numeric PIN entry with individual character boxes.
- type: field name: verification_code properties: widget: PinInput length: 6| Property | Description |
|---|---|
length | Number of input boxes (default: 6) |
type | number (default) or alphanumeric |
mask | Hide input with asterisks |
Display Widgets
Section titled “Display Widgets”Plain text display.
- type: field name: name properties: widget: Text fw: "600" size: lg c: dimmed| Property | Description |
|---|---|
fw | Font weight (400-900) |
size | xs, sm, md, lg, xl |
c | Color (dimmed, or hex) |
lineClamp | Max lines to show |
html | Render the value as raw HTML instead of plain text (e.g. a formatted address) |
Rendering raw HTML: set html: true to inject the field value as HTML — useful for
server-formatted blocks such as a multi-line address.
- type: field name: contact_address properties: widget: Text nolabel: true html: true c: dimmed size: smApp views have no separate Html widget — raw-HTML display is the html prop on Text
(portal views do use widget: Html). The HTML must come from trusted server-side rendering.
Colored badge.
- type: field name: status properties: widget: Badge variant: dot size: sm colors: draft: gray active: green archived: red| Property | Description |
|---|---|
variant | filled, outline, dot |
size | Badge size |
colors | Color mapping by value |
labels | Display-text mapping by stored value — e.g. {"true": "Passed", "false": "Failed"} to relabel a boolean, or {"Draft": "Not sent"} to reword a choice. Unmapped values render as stored, except a Boolean, which renders Yes/No. |
A Boolean never renders its stored true/false as text. With no labels it reads
Yes/No — honest under a column header that already names the field, but say what the
states mean whenever the field carries a verdict:
- type: field name: is_passed properties: widget: Badge label: Result labels: {'true': Passed, 'false': Failed} colors: {'true': green, 'false': red}On a card with no column header, name the on state and hide the off one:
- type: field name: is_billing properties: widget: Badge labels: {'true': Billing} visible: Q(is_billing=True) colors: {'true': blue}colors keys stay on the stored value ('true', 'false', "Draft"), so relabelling
never breaks the colour mapping. A boolean status ribbon
differs deliberately: there the dot colour carries on/off, so its label names the dimension
and defaults to the humanized field name.
Renders a field’s value as a Mantine Alert banner. Because the body text comes
from the field, a computed message field can drive case-specific wording,
and the alert self-hides when the field is empty — no visible rule needed.
Presentation (title, color, variant, icon) is fixed on the field’s
properties.
- type: field name: reservation_notice properties: widget: Alert title: Insufficient stock color: orange variant: light icon: AlertTriangleBack it with a calculated field that returns the message (or "" to hide it):
reservation_notice = Text(calculate="compute_reservation_notice", store=False)
@Model.calculate("is_fully_reserved", "lines")async def compute_reservation_notice(self): for record in self: record.reservation_notice = "" if record.is_fully_reserved else "No stock available to reserve…"| Property | Description |
|---|---|
title | Optional static heading above the message |
color | Mantine color (e.g. orange, red, blue) |
variant | light, filled, outline, transparent |
icon | Lucide icon name (e.g. AlertTriangle) |
For a fixed banner whose text never varies, use the static - type: alert
element instead of a field-bound widget.
Display a many-to-many as coloured pills.
- type: field name: tags properties: widget: Tags size: sm| Property | Type | Default | Description |
|---|---|---|---|
size | String | "xs" | Pill size |
colorField | String | "color" | Field on each row holding its colour |
Colours come in two shapes, and both work. A palette index (an Integer field, as on
CrmTag.color) picks from the built-in tag palette. A hex string ("#FA5252" — a Char
edited with ColorInput) is used verbatim, with the text colour chosen by luminance so a
label stays readable on it. Anything else falls back to the first palette colour.
AvatarGroup
Section titled “AvatarGroup”The people on a record, as overlapping faces. For a relation to User — assignees, members,
attendees — where a row of spelled-out names would compete with everything else on the card.
- type: field name: assignees properties: widget: AvatarGroup size: sm max: 4| Property | Type | Default | Description |
|---|---|---|---|
size | String | "sm" | Avatar size |
max | Number | 4 | Faces before the rest collapse into a +N circle |
Each face resolves its own image and falls back to initials, exactly as Avatar does; the
overflow circle names the remainder on hover, so the count can still be interrogated.
Display-only — the record’s own form is where membership changes.
Modes: kanban, list, formlist, display.
Avatar
Section titled “Avatar”Display-only avatar widget with automatic image fetching. Shows user initials when no image is available.
- type: field name: user_id properties: widget: Avatar avatarField: avatar size: md| Property | Type | Default | Description |
|---|---|---|---|
avatarField | String | "avatar" | Field name on value object containing attachment ID |
size | String | "md" | Avatar size (xs, sm, md, lg, xl) |
radius | String | "xl" | Border radius |
color | String | "initials" | Background color for initials |
How it works:
The widget automatically fetches avatar images when:
- Value is an object with an attachment ID field (specified by
avatarField) - Value itself is an attachment ID (number)
- Fallback fields:
image,avataron the value object
Example - Display user avatar in a list:
- type: field name: assigned_to properties: widget: Avatar avatarField: avatar size: smDisplay image.
- type: field name: image properties: widget: Image h: 100 w: 100 fit: cover| Property | Description |
|---|---|
h | Height in pixels |
w | Width in pixels |
fit | cover, contain, fill |
initials_field | Sibling field seeding an auto-coloured initials avatar when there’s no image (same rules as ImageInput) — applies to list, kanban, and display cells |
Rating
Section titled “Rating”Star rating display.
- type: field name: rating properties: widget: Rating count: 5Display a Lucide icon.
- type: field name: icon_name properties: widget: Icon size: 24 color: blue| Property | Description |
|---|---|
size | Icon size in pixels |
color | Mantine color (e.g., blue, red.6) |
BoxedIcon
Section titled “BoxedIcon”Icon with colored background box. The field’s value is the icon name; the tile around it can
be tinted per record by naming a sibling colour field with background_field.
- type: field name: icon_name properties: widget: BoxedIcon w: 80 size: 34 borderRadius: 12px background_field: color # a sibling field holding this record's colour| Property | Description |
|---|---|
w / h | Tile size (square; either one sets it) |
size | Icon size in pixels |
color | Icon colour (default white) |
background | Static tile background |
background_field | Field whose value tints the tile — beats background |
borderRadius | Tile corner radius |
The colour field named by background_field must itself be on the view (add it with
visible: false if it should not be displayed on its own).
ProgressBar
Section titled “ProgressBar”Progress bar display.
- type: field name: completion properties: widget: ProgressBar color: blue size: md| Property | Description |
|---|---|
color | Mantine color |
size | xs, sm, md, lg, xl |
striped | Show striped pattern |
animated | Animate the stripes |
A link value rendered as what it is: a clickable anchor that opens in a new tab, with a one-click copy button beside it. Long URLs wrap rather than overflow, so it is safe in a narrow column or a settings row. Read-only — it displays a URL, it never edits one.
- type: field name: payment_url properties: widget: Url label: Payment Link| Property | Description |
|---|---|
linkLabel | Friendly text to show instead of the raw URL |
copy | false hides the copy button (default true) |
justify | center / flex-end — alignment of the link + copy within the field |
Reach for this instead of a read-only TextInput plus a “Copy Link” button: copying is the
widget’s own affordance, and a button that only copies has no way to do the copy from the
backend anyway (an action result cannot reach the clipboard).
SummaryIndicator
Section titled “SummaryIndicator”A compact, quiet detail indicator: a single icon (chosen in the view via icon)
tinted by a status colour, that opens a popover with a labelled breakdown on
click. Meant for a per-row signal that stays out of the way until you want the
detail — stock availability on a quote line, fulfillment progress on a confirmed
one — without spending two or three columns on the figures.
Bind it to a field that returns a summary object (or null to render nothing,
so non-applicable rows stay blank). The popover has no fixed subject: it renders
whatever rows the payload carries.
{ "status": "Partial", "color": "orange", "uom": "Units", "rows": [ { "label": "Available", "value": 3 }, { "label": "On hand", "value": 3 }, { "label": "Expected", "value": "2026-09-12", "type": "date" }, { "divider": true, "label": "Not Invoiced" }, { "label": "Warehouse", "value": "Main Warehouse" } ]}| Payload key | Description |
|---|---|
status | Headline text — also the icon’s tooltip, and the inline text next to the icon on a form |
color | Mantine colour name (or hex) for the icon tint and the headline |
uom | Default unit appended to numeric row values; a row may override it with its own uom |
rows | Ordered list of {label, value} rows. type: "date" renders an ISO date in the viewer’s locale; numbers get locale digit grouping. A {divider: true, label} entry draws a separator with an optional sub-heading, so one payload can carry two sections |
- type: field name: stock_summary properties: widget: SummaryIndicator icon: PackageSearch nolabel: true| Property | Description |
|---|---|
icon | Lucide icon name for the indicator (defaults to Info) |
nolabel | On a list/formlist column, blanks the header (see List Views) — keeps an icon-only column tight and unlabelled |
Row labels and status are authored server-side as English text, which is what
the translation layer keys on, so they translate like any other string (and fall
back to the English they already are when uncatalogued). On a form the icon is
paired with its label and status text inline; in list/formlist cells it
renders as the bare icon.
----------|-------------|
| icon | Lucide icon name for the indicator (defaults to PackageSearch) |
| nolabel | On a list/formlist column, blanks the header (see List Views) — keeps an icon-only column tight and unlabelled |
The icon colour comes from the payload’s color; the popover lists Available / On
hand / Reserved / Incoming / Expected / Warehouse (rows with no value are omitted). On
a form the icon is paired with its label and status text inline; in list/formlist
cells it renders as the bare icon.
Dashboard Widgets
Section titled “Dashboard Widgets”Dashboard widgets display KPIs, metrics, and summary data in form views. They support locale-aware number formatting using the user’s language settings.
:::info Display Widget Properties
Dashboard and display widgets (Card, CompactList, Charts) can specify currencyField directly in view properties because they display arbitrary data rather than mapping to model fields. For form fields bound to model definitions, use currency_field on the model instead. The prefix and suffix properties are always static strings.
:::
Displays a KPI/metric card with value, label, icon, and optional click action. Perfect for dashboard overviews.
- type: field name: total_revenue properties: widget: Card label: Total Revenue icon: DollarSign color: green format: currency currencyField: currency method: action_view_revenue_details| Property | Type | Default | Description |
|---|---|---|---|
label | String | "Value" | Display label above the value |
icon | String | "ChartBar" | Lucide icon name |
color | String | "blue" | Mantine color for icon/hover |
format | String | - | Format type: currency, number, integer, percent |
currencyField | String | - | Field path containing currency object (e.g., "currency", "company__currency") |
prefix | String | - | Static prefix text (e.g., "$", "€") |
suffix | String | - | Static suffix text (e.g., "%", "kg") |
precision | Number/String | 2 | Decimal places (static int) or field path (e.g., "uom__rounding") |
method | String | - | Action method to call when clicked |
CompactList
Section titled “CompactList”A compact table/list for displaying summary data like top customers, recent items, etc.
Model field:
top_customers = JSON(description="Top Customers", default=[])View usage:
- type: field name: top_customers properties: widget: CompactList currencyField: currency columns: - field: name label: Customer flex: 2 bold: true - field: count label: Orders align: center format: integer - field: amount label: Total format: currency color: blue bold: true method: action_view_customerCompactList Properties:
| Property | Type | Default | Description |
|---|---|---|---|
columns | Array | Auto-generated | Column definitions (see below) |
currencyField | String | - | Field path containing currency object (e.g., "currency") |
prefix | String | - | Static prefix text (e.g., "$", "€") |
suffix | String | - | Static suffix text (e.g., "%", "kg") |
showHeader | Boolean | true | Show column headers |
emptyText | String | "No data available" | Text when list is empty |
method | String | - | Action method called on row click (receives item_id, item) |
Column Properties:
| Property | Type | Default | Description |
|---|---|---|---|
field | String | Required | Key in the data object |
label | String | Field name | Column header label |
format | String | "text" | text, currency, number, integer, percent, badge |
precision | Number/String | 2 | Decimal places (static int) or field path |
align | String | Auto | left, center, right (numbers default to right) |
flex | Number | 1 | Flex grow value |
width | String | - | Fixed column width |
color | String | - | Mantine color for text |
bold | Boolean | false | Bold text |
badgeColor | String | "gray" | Badge color when format is badge |
LinkedRecords
Section titled “LinkedRecords”Displays related records from a JSON field as cards with customizable layout and row actions. Useful for showing credit notes, related documents, or any list of linked items with action buttons.
Model field:
outstanding_credits = JSON(description="Outstanding Credits", default=[])View usage:
- type: field name: outstanding_credits properties: widget: LinkedRecords title: Available Credits content: - type: row content: - type: field name: record_type properties: widget: Badge color: blue - type: field name: number properties: fw: 600 - type: row content: - type: field name: date properties: widget: Date c: dimmed - type: field name: amount properties: widget: Float currency: currency rowActions: - icon: ExternalLink method: action_view tooltip: View - icon: Check method: action_apply tooltip: Apply confirm: Apply this credit?LinkedRecords Properties:
| Property | Type | Default | Description |
|---|---|---|---|
title | String | - | Title displayed above the cards |
content | Array | Auto-generated | Layout definition with rows and fields |
rowActions | Array | [] | Action buttons for each card (see below) |
emptyText | String | "No records" | Text when list is empty |
hideWhenEmpty | Boolean | true | Hide widget when no records |
maxHeight | Number | - | Maximum height with scroll |
visible | Boolean/String | - | Visibility (Q-expression supported) |
readonly | Boolean/String | - | Hide actions when true |
groups | Array | - | Group-based access control |
Row Action Properties:
| Property | Description |
|---|---|
icon | Lucide icon name — required, a row action is icon-only |
method | Model method to call with that record |
tooltip | Hover label for the icon |
visible | Boolean or Q-expression, evaluated per card |
disabled | Boolean or Q-expression — greyed instead of hidden (hiding vs disabling) |
hint | Reason shown instead of tooltip while the action is disabled |
confirm | Ask before running the method |
rowActions: - icon: Check method: action_apply tooltip: Apply disabled: Q(state='Applied') hint: This credit has already been applied.Field Widgets in Content:
| Widget | Description |
|---|---|
Text | Default text display |
Badge | Colored badge (color property) |
Date | Formatted date |
Float | Formatted number with currency/prefix/suffix support |
Float Field Properties:
| Property | Description |
|---|---|
currency | Field name containing currency object (symbol, position, rounding) |
prefix | Static string or field path (e.g., "uom__name") |
suffix | Static string or field path |
precision | Number or field path for decimal places |
Row Action Properties:
| Property | Type | Description |
|---|---|---|
icon | String | Lucide icon name |
method | String | Action method to call (receives item_id, item) |
tooltip | String | Hover tooltip |
color | String | Icon color |
confirm | String / Object | Ask before the method runs — same grammar as a form button’s confirmation prompt |
visible | String | Simple Q-expression for conditional visibility |
Chart Widgets
Section titled “Chart Widgets”Chart widgets display visual data representations. They work with JSON fields containing numeric arrays or structured data. All chart widgets support currency formatting and locale-aware number display.
Sparkline
Section titled “Sparkline”A compact inline chart perfect for showing trends in list views, kanban cards, and forms. Expects a JSON field with an array of numbers.
Model field:
sales_trend = JSON(description="Sales Trend", default=[])View usage:
- type: field name: sales_trend properties: widget: Sparkline w: 120 h: 24 color: blue curveType: linear| Property | Type | Default | Description |
|---|---|---|---|
w | Number | 100 | Width in pixels |
h | Number | 30 | Height in pixels |
color | String | "blue" | Mantine color (e.g., "teal", "red.6") |
curveType | String | "linear" | Curve type: linear, bump, natural, monotone, step |
strokeWidth | Number | 2 | Line stroke width |
fillOpacity | Number | 0.2 | Fill opacity (0-1) |
withGradient | Boolean | true | Use gradient fill |
trendColors | Object | - | Dynamic colors based on trend (see below) |
Trend Colors:
Use trendColors instead of color to change the chart color based on trend direction:
- type: field name: performance_data properties: widget: Sparkline trendColors: positive: teal negative: red neutral: gray- positive: Color when first value < last value (upward trend)
- negative: Color when first value > last value (downward trend)
- neutral: Color when first value = last value
BarChart
Section titled “BarChart”Bar chart for visualizing data with multiple series.
Model field:
monthly_data = JSON(description="Monthly Data", default=[])Data format:
[ {"month": "Jan", "sales": 100, "costs": 60}, {"month": "Feb", "sales": 120, "costs": 70}, {"month": "Mar", "sales": 90, "costs": 55}]View usage:
- type: field name: monthly_data properties: widget: BarChart dataKey: month h: 150 currencyField: currency| Property | Type | Default | Description |
|---|---|---|---|
dataKey | String | Required | Field name for X-axis labels |
h | Number | 150 | Chart height in pixels |
currencyField | String | - | Field path containing currency object (e.g., "currency", "company__currency") |
prefix | String | - | Static prefix text (e.g., "$", "€") |
suffix | String | - | Static suffix text (e.g., "%", "kg") |
precision | Number/String | 2 | Decimal places (static int) or field path (e.g., "uom__rounding") |
The chart automatically generates series from all numeric fields in the data (excluding the dataKey field).
AreaChart
Section titled “AreaChart”Area chart with the same properties as BarChart.
- type: field name: trend_data properties: widget: AreaChart dataKey: date h: 200 currencyField: currency precision: 0Values in chart tooltips are formatted using the user’s locale settings (decimal and thousands separators from Language profile).
Embedded View Widgets
Section titled “Embedded View Widgets”Embedded list view for OneToMany fields.
- type: field name: order_lines properties: widget: List view: order_line_list_view create: true delete: true ctx: form_identifier: order_line_form_view default_qty: 1| Property | Description |
|---|---|
view | List view identifier to use |
create | Allow creating new records |
delete | Allow deleting records |
editable | true for inline editing, "modal" for modal editing |
limit | Maximum rows to display initially (default: 100). Shows “Load more” button when exceeded |
context | Default values and view references (see Field-Level Context) |
Pagination
Section titled “Pagination”For large OneToMany lists, use the limit property to enable client-side pagination:
- type: field name: order_lines properties: widget: List editable: true limit: 50When rows exceed the limit:
- Only the first
limitrows are displayed initially - A “Load X more (Y remaining)” button appears on the right
- Clicking the button loads the next batch of rows
- Pagination resets when navigating to a different record
Column Properties (Q-Expression Support)
Section titled “Column Properties (Q-Expression Support)”List columns (fields in the List view) support visible, readonly, required, and filter properties with Q-expression evaluation. These are evaluated per-row against the row data.
- type: field name: discount_amount properties: widget: NumberInput visible: Q(has_discount__eq=true) readonly: Q(is_locked__eq=true) required: Q(amount__gt=0)Embedded lists also receive the child model’s model metadata. When the parent record is locked by controlled edits, the list structure remains locked, but child fields listed in the child model’s controlled-edit exclusions can still be edited inline.
Parent Data Access:
Use the _parent. prefix to access fields from the parent form (the form containing the List widget):
- type: field name: account properties: widget: DataCombo filter: Q(companies__in=[_parent.company]) | Q(companies__isnull=True)Nested Field Access:
Use __ for traversing relationships (Django ORM style):
- type: field name: price properties: readonly: Q(_parent.contact__company__locked__eq=true)| Property | Type | Description |
|---|---|---|
visible | Boolean/String | Per-row visibility. false hides cell, Q-expression evaluated per-row |
readonly | Boolean/String | Per-row readonly. true or Q-expression |
required | Boolean/String | Per-row required. true or Q-expression |
filter | String | Q-expression filter for DataCombo/MultiCombo. Supports _parent. prefix |
Kanban
Section titled “Kanban”Embedded kanban view.
- type: field name: tasks properties: widget: Kanban view: task_kanban_viewWidget Property Reference
Section titled “Widget Property Reference”Common Properties
Section titled “Common Properties”| Property | Type | Description |
|---|---|---|
label | string | Field label |
nolabel | boolean | Hide label |
placeholder | string | Placeholder text |
size | string | Size variant |
visible | Boolean/String | Show/hide field. Can be false or Q-expression |
readonly | Boolean/String | Make field read-only. Can be true or Q-expression |
required | Boolean/String | Make field required. Can be true or Q-expression |
groups | Array | Group identifiers for access control |
Example with visibility/required:
- type: field name: price properties: widget: NumberInput readonly: true required: Q(type__eq='product')See Element Properties for details.
Style Properties
Section titled “Style Properties”| Property | Type | Description |
|---|---|---|
fw | string/number | Font weight |
c | string | Text color |
mt | string | Margin top |
mb | string | Margin bottom |
style | object | Custom CSS |
Field-Level Context
Section titled “Field-Level Context”Field-level context allows you to pass default values and configuration to related record creation. This is useful for:
- Pre-populating fields when creating records from embedded lists
- Setting defaults when using “Create & Edit” in DataCombo/MultiCombo
- Specifying which form view to use for modal editing
Syntax
Section titled “Syntax”Use the context property with default_<fieldname> keys:
- type: field name: order_lines properties: widget: List ctx: default_qty: 1 default_uom: 5 form_identifier: order_line_form_viewSupported Context Keys
Section titled “Supported Context Keys”| Key Pattern | Description |
|---|---|
default_<field> | Pre-populate field with value when creating new records |
form_identifier | Form view identifier for modal editing |
| Custom keys | Available via self._ctx in backend methods |
The full context object is passed to the backend and is accessible via self._ctx on model instances.
default_<field> values: literals and field references
Section titled “default_<field> values: literals and field references”A default_<field> value is either a literal (default_qty: 1,
default_type: Opportunity, default_is_customer: true) or a reference to a field on
the record the widget is rendered on, which is resolved to that field’s value before the
new record is created. References use the same grammar as filter: — a bare field name,
__ to traverse a relation, _parent. to reach the parent form from a line:
- type: field name: lot properties: widget: DataCombo create: true filter: Q(product=product) ctx: default_product: product # → the id of this record's `product` default_category: product__category # → traverses the relation default_company: _parent.company # → the parent form's companyA string that doesn’t name a field on the record stays a literal, so
default_type: Opportunity is unambiguous. A reference to a field that is empty is
dropped rather than sent as null — the created record keeps its own default instead of
being blanked.
Every field type can be referenced, and each is carried in the shape a write expects:
| Referenced field | What the prefill carries |
|---|---|
| Char, Text, Selection, Integer, Float, Monetary, Boolean, Date, Datetime | the value itself |
| ManyToOne, OneToOne | the record’s id — resolved back to the record, so the field shows its name |
| ManyToMany, OneToMany | command notation, [["link", [ids]]] (link these records) |
A literal follows the same rules: default_qty: 1, default_is_customer: true, and a
relational literal is written in command notation
(default_lines: [["create", {"qty": 1}]]) — a bare list of ids is silently discarded on save.
This is what makes inline creation valid: a combo filtered to Q(product=product) offers
only that product’s records, so a record created from it must be born with the same
product. Without the prefill, the create fails on a required field (or silently lands in
the wrong scope).
Examples
Section titled “Examples”List widget with defaults:
- type: field name: invoice_lines properties: widget: List editable: true ctx: default_quantity: 1 default_tax_included: trueDataCombo with Create & Edit defaults:
When users click “Create & Edit” in a DataCombo, the context values are passed to the new record form:
- type: field name: contact properties: widget: DataCombo create: true ctx: default_is_customer: true default_company: 5Backend Access
Section titled “Backend Access”Context is passed to the backend when creating new records. The backend processes default_* in _default_get and makes the full context available via self._ctx:
class OrderLine(Model): async def _default_get(cls, context=None): """Called during record creation with field-level context.""" defaults = await super()._default_get(context) # default_* keys are automatically applied by the base method # Access custom context values for additional logic if context and context.get('custom_flag'): defaults['some_field'] = compute_default() return defaults
async def compute_some_field(self): """Access context in calculated fields or any method.""" # self._ctx contains the full context passed from frontend if self._ctx.get('default_currency'): # Use context value passNext Steps
Section titled “Next Steps”- View Inheritance - Extending views
- Form Views - Using widgets in forms