An Odoo addon becomes maintainable when its domain model, security, user interface, and external delivery contract are designed together.
Model the editorial domain
Instead of forcing portfolio content into generic notes, define models such as profile, experience, education, project, skill group, certification, and volunteering. Each model gets fields that match the decisions an editor actually makes.
class Project(models.Model):
_name = "portfolio.project"
name = fields.Char(required=True)
summary = fields.Text(required=True)
challenge = fields.Html()
solution = fields.Html()
outcome = fields.Html()
published = fields.Boolean(default=True)Security is part of the UX
Editors need a small, understandable permission group. Access rules and model ACLs should make it obvious who can read or change portfolio data. Superuser convenience should not be the only reason the interface works.
Ordering is editorial behavior
Sequence fields are simple, but they encode an important product decision: which project appears first, how jobs are arranged, and how skills are grouped. Treat ordering as content, not as a frontend detail.
One transformation layer
A dedicated controller should transform ORM records into a frontend-oriented payload. This keeps Odoo field names and Astro component concerns from leaking into each other.
- Filter by publication state.
- Flatten relational display values.
- Normalize image and file URLs.
- Return dates in a stable format.
Upgrade against the installed framework
Framework migrations are concrete. Odoo 19 changes CLI names, view types, group privileges, request access, and blog fields. Read the installed source and verify against its schema rather than relying on an older tutorial.