When customizing forms in Odoo, it’s quite common to use default_get() to automatically fill certain fields when creating a new record. In most situations, this works exactly as expected.
But things can get confusing when that field is marked as readonly.
You might have seen this happen:
- You define a field as readonly
- You populate its value using default_get()
- The value appears correctly when the form opens
- You save the record
- And suddenly… the value disappears
At first glance, this feels like a bug. But in reality, it’s just how Odoo’s web client is designed to work.
What’s Actually Happening Behind the Scenes?
When you open a new form view, Odoo calls the default_get() method to populate default values. These values are shown in the user interface — even for readonly fields.
However, during the save operation, the web client only sends back values that are considered editable or modified by the user.
Since readonly fields:
- Cannot be edited
- Are not changed by user interaction
They are treated as unchanged values, and therefore:
👉 They are not included in the data sent to the server when the record is saved.
As a result, the ORM never receives those values — and they are not stored in the database.
That’s why the value disappears after saving.
Reproducing the Issue
Python Model
from odoo import models, fields, api
class StudentRegistration(models.Model):
_name = "student.registration"
name = fields.Char(string="Student Name")
reference_code = fields.Char(string="Reference Code", readonly=True)
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
res['reference_code'] = "REF-001"
return res
XML View
<field name="reference_code" readonly="1"/>
Now:
- Open the form
- You’ll see the Reference Code is filled
- Click Save
- The value disappears
This happens because the readonly field was never sent back to the server during record creation.
UI-Level Fix: Using force_save="1"
If you want the readonly value from the form to be saved, you can instruct the web client to include it during submission by adding:
<field name="reference_code" readonly="1" force_save="1"/>
With this:
- The value is included in the save request
- The ORM receives it
- The value is stored correctly
- It remains visible after saving
This approach is useful when the value is mainly related to UI behavior and is expected to be created from the form itself.
Recommended Backend Approach: Assign Value in create()
If the value is important for business logic — like an internal reference, identifier, or workflow dependency — it’s better to assign it at the ORM level instead of relying on the form view.
Here’s how you can do that:
@api.model
def create(self, vals):
if not vals.get('reference_code'):
vals['reference_code'] = "REF-001"
return super().create(vals)
This ensures the value is stored:
- Even if the record is created via API
- During CSV import
- From automated cron jobs
- Through external integrations
In other words, it works regardless of how the record is created — not just from the form view.
When Should You Use What?
Use force_save when:
- The field is readonly in the UI
- The value is assigned through the form
- The value is display-oriented
Use create() override when:
- The value is business-critical
- It must always be stored
- The record may be created through automation or integrations
When a readonly field is populated using default_get(), it may disappear after saving because readonly fields are not included in the form submission sent to the server.
This is expected behavior in Odoo — not a bug.
To make sure the value persists:
- Use force_save="1" for form-level persistence
- Or assign the value in create() for backend reliability
Understanding this small detail can save a lot of debugging time when developing custom Odoo modules.