Skip to main content
iVentureTeam

attachment_image

The picture on a project task's kanban card is not a binary field. It is attachment_image: a tiny display-only widget that turns a many2one to ir.attachment into a 300x300 image.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 25, 2026Updated August 25, 20265 min read
Odoo 19 project kanban card showing a task cover image rendered by the attachment_image widget from the displayed_image_id field.
Technical nameattachment_image
Field typesmany2one
Viewskanban (core usage); renders wherever the field is placed
Moduleweb, present in every Odoo database
Used in core3 occurrences across 2 modules, including project, project_todo
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. Applied in view XML; Project's Set Cover Image menu manages the underlying field
Alternativesimage, image_url, background_image

What the Attachment Image field does

Most images in Odoo live in binary fields on the record itself. attachment_image covers the other pattern: the record merely points at an existing attachment through a many2one, and the widget renders that attachment as a picture.

The whole component is one template. If the many2one holds a value, it emits an <img> whose source is /web/image/<attachment id>/300x300?unique=1, which asks the server for a version of the attachment scaled to fit inside 300x300 pixels. If the field is empty, the widget renders an empty container and nothing else. There is no upload button, no dialog, no zoom: the source declares supportedTypes: ["many2one"], no options, and no behavior beyond the template.

Its one core job is the kanban cover image. project.task.displayed_image_id is a many2one to ir.attachment restricted to images attached to the task, and the Project, project sharing and To-do kanban cards all render it with this widget.

What this means for your team

Cover images are the difference between a kanban board that reads like a spreadsheet and one your team can scan at a glance. A creative agency drops the approved visual on each task card; a property manager shows the unit photo on each maintenance ticket; a manufacturer shows the defect photo on each quality issue. The mechanism is exactly this widget plus a many2one to the record's attachments.

The design has a quiet advantage over binary fields: the image is stored once as an attachment and only referenced by the card, so choosing a different cover never duplicates the file. It also means the picture must already exist as an attachment on the record before it can be shown, which is why the Set Cover Image action in Project opens the task's attachments rather than a file picker.

Working examples

The core pattern: task cover image

<!-- inside a kanban card template -->
<field t-if="record.displayed_image_id.value"
       name="displayed_image_id"
       widget="attachment_image"/>

This is verbatim how Project renders covers. The t-if guard skips the element entirely for tasks without a cover, which keeps the card compact.

A custom cover field on your own model

<field name="cover_attachment_id" widget="attachment_image"/>

Where cover_attachment_id is a many2one to ir.attachment, typically with a domain limiting it to image attachments of the current record, mirroring displayed_image_id on project.task.

The /web/image route does the real work

The URL the template builds explains most surprises. /web/image/<id>/300x300 is the generic attachment image route with a resize instruction: the server scales the image down to fit inside the box while keeping proportions, and never scales up. The ?unique=1 suffix is a fixed cache-busting marker, so swapping the cover shows the new picture without a hard refresh.

Because the route resolves the id as an ir.attachment, pointing the many2one at any other model renders broken images, and a user who cannot read the attachment gets the placeholder the route serves for inaccessible records. The tooltip is deliberately quiet: t-att-title is gated on env.debug, so file names only appear when developer mode is on, a small detail added in 17 along with the 300x300 resize. In Odoo 16 the template requested the attachment at full size and always showed the tooltip; 17 tightened both, and nothing has changed since.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Development branch shows internal changes only; see below.
Odoo 19.0VerifiedVerified against the shipped source; many2one value access updated internally.
Odoo 18.0VerifiedSame behavior: 300x300 resize, debug-only tooltip.
Odoo 17.0Verified300x300 server resize and debug-only tooltip introduced here.
Odoo 16.0VerifiedRenders the attachment at full size and always shows the file-name tooltip.

Upgrade note. Nothing to migrate between 16 and 19: the widget name and the XML are identical. Cards may look lighter after a 16 to 17 upgrade because the server starts serving 300x300 thumbnails instead of full-size files, which is a bandwidth win, not a regression.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. We read this widget's file on the public development branch at the time of writing, with the usual caveat that the branch is unstable and anything can still change before release.

No functional changes: the template still renders the same 300x300 attachment URL with the debug-only tooltip. The only movement is the migration to the new Owl props syntax, which affects JavaScript patches, not XML. We re-verify this page once Odoo 20 ships.

Common problems and fixes

SymptomCause and fix
Nothing renders at allThe many2one is empty; the widget's template only emits an img when the field has a value. Set a cover attachment on the record, or guard the element with t-if like core does.
Broken image iconThe many2one does not point at ir.attachment, or points at a non-image attachment. Restrict the field to image attachments of the record, mirroring displayed_image_id's domain.
Image looks small or blurryThe server scales the attachment to fit 300x300 and never upscales; tiny sources stay tiny. Attach a larger image; for other sizes, use the image widget on a binary field instead.
No file name on hoverThe tooltip is rendered only when developer mode is active. Enable developer mode, or accept it: users are not meant to see attachment names here.
Options in the view have no effectThe widget declares no supported options; the options dict is ignored. Use the image widget for size, zoom or preview options.

Attachment Image field vs the alternatives

WidgetBest forKey difference
attachment_imageShowing one of the record's attachments as a picture, the kanban cover image patternReads a many2one to ir.attachment; display only, fixed 300x300 server resize
imageA picture stored on the record itself, with upload, zoom and size optionsWorks on binary fields and is fully editable; attachment_image only displays a reference
image_urlDisplaying an image hosted at a URL stored in a char fieldTakes the address from field text instead of an attachment id
background_imageA binary image as a decorative backgroundSame lazy img rendering as image but without any editing chrome

The choice is storage first, widget second: if the picture belongs to the record itself, use a binary field with image; if it is one of the record's attachments being promoted to a display slot, that is exactly attachment_image.

Frequently asked questions

How do I set the cover image on a project task?+
On the kanban card's menu choose Set Cover Image, then pick among images already attached to the task. The selection is stored in displayed_image_id, which the card renders through attachment_image.
Why must the picture be attached to the record first?+
The field is a many2one to ir.attachment, so it can only reference an existing attachment. Upload the image to the record (the chatter does this), then select it as cover.
Can I change the 300x300 size?+
Not from the view: the size lives inside the widget's template, and there are no options. A custom module can inherit the template, or you can switch to the image widget on a binary field, which exposes a size option.
Does attachment_image work in form views?+
Yes, it renders the same static image wherever the field is placed; core just happens to use it only on kanban cards. In a form you usually want the richer image widget instead.
Why do I see a placeholder instead of the image for some users?+
The /web/image route enforces access rights on the attachment. Users who cannot read the attachment record get the placeholder, so check the attachment's ownership and the record rules on its linked document.

Want kanban boards your team can scan in seconds?

Cover images, badges and card layout are small view changes that transform daily boards, and they need someone at home in Odoo's kanban XML. We build exactly this kind of board polish, custom cover fields included, in our Odoo customization work on 16 through 19.

Book a free consultation

How this page was produced

Every statement on this page was read from the Odoo 19.0 source: attachment_image_field.js and its template in the web module, plus the three core usages in project and project_todo views. Version notes come from diffing the same files on the 16.0, 17.0 and 18.0 branches, and the Odoo 20 section from the public development branch. Spotted an error? Tell us and we will correct the page.