Mastering Odoo Context-Aware Fields: Unleash 3 Advanced Techniques with @api.depends_context
Welcome to a comprehensive guide on unlocking the full potential of Odoo context-aware fields. In the world of Odoo development, creating dynamic, responsive, and intuitive user interfaces is paramount. Often, the visibility or behavior of a field, button, or even an entire section of a view needs to change based on the user’s current session, the origin of navigation, or specific data stored in the context. This is where @api.depends_context becomes your invaluable ally.
This powerful decorator allows you to define fields whose values are computed based on the Odoo context, rather than just other fields in the record. Unlike the more commonly used @api.depends, which reacts to changes in record data, @api.depends_context listens for shifts in the ambient environment or user session, providing a robust mechanism for truly intelligent and adaptable applications.
This blog post is inspired by and expands upon the concepts introduced in this insightful video tutorial: https://youtu.be/TZ-083rLNFg. We will walk through practical, step-by-step examples, transforming complex requirements into elegant Odoo solutions. By the end of this guide, you will be proficient in leveraging Odoo context-aware fields to create more sophisticated and user-friendly Odoo modules.
Why Context-Aware Fields are Essential for Odoo Development
Traditional computed fields in Odoo rely on the values of other fields within the same record or related records. While incredibly useful, they fall short when your logic needs to consider broader factors such as:
- Where the user initiated an action (e.g., coming from a specific menu item).
- The current logged-in user or their role.
- The active company or language.
- Custom flags passed through actions.
Odoo context-aware fields, powered by @api.depends_context, bridge this gap. They allow your application to react intelligently to the “who, what, and where” of the user’s interaction, leading to:
- Improved User Experience (UX): Displaying only relevant information and actions, reducing clutter.
- Enhanced Security:** Dynamically showing/hiding sensitive actions based on user roles or specific scenarios.
- Streamlined Workflows: Guiding users through processes by showing contextually appropriate options.
- Reduced Duplication:** Reusing the same views for different purposes by leveraging conditional logic.
Let’s dive into practical scenarios to see @api.depends_context in action.
Scenario 1: Dynamic Button Visibility Based on Origin View – Adding Students to a Class
Imagine a scenario in an educational management system where you have a list of all students. Normally, you wouldn’t want an “Add to Class” button visible when viewing the general student list. However, if a user navigates to this list specifically from a “Class” record, intending to add students to that particular class, then the button should appear, enabling them to link students directly. This is a classic case for Odoo context-aware fields.
Goal: Display an “Add to Class” button in the student list view only when accessed from a specific class’s “Add Student” action.
Step-by-Step Implementation:
1. Create/Modify the Class Model and View:
First, ensure your class.model exists. We’ll add a method to this model that, when called, will open the student list view. Crucially, this method will pass specific context keys.
models/class_model.py:
from odoo import models, fields, api
class ClassModel(models.Model):
_name = 'class.model'
_description = 'Class Information'
name = fields.Char(string="Class Name", required=True)
student_ids = fields.Many2many('student.model', string="Students in Class")
teacher_id = fields.Many2one('hr.employee', string="Assigned Teacher") # Assuming hr.employee for teacher
def action_add_student(self):
"""
Opens the student list view with specific context to enable
the 'Add to Class' button and pre-select the active class.
"""
return {
'name': 'Select Students to Add',
'type': 'ir.actions.act_window',
'res_model': 'student.model',
'view_mode': 'list,form',
'domain': [], # Optionally add domain to filter students
'context': {
'add_to_class_mode': True, # Custom context key for dynamic behavior
'active_class_id': self.id, # Pass the current class ID
},
'target': 'current', # Open in the same window
}
views/class_views.xml:
Add a button in the class form view that triggers the action_add_student method:
<record id="view_class_form" model="ir.ui.view">
<field name="name">class.model.form</field>
<field name="model">class.model</field>
<field name="arch" type="xml">
<form string="Class">
<header>
<button name="action_add_student" string="Add Students" type="object" class="oe_highlight" />
</header>
<sheet>
<group>
<field name="name"/>
<field name="teacher_id"/>
</group>
<notebook>
<page string="Students">
<field name="student_ids" widget="many2many"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_class_model" model="ir.actions.act_window">
<field name="name">Classes</field>
<field name="res_model">class.model</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_class_root" name="Education" sequence="10"/>
<menuitem id="menu_class_model" parent="menu_class_root" name="Classes" action="action_class_model" sequence="10"/>
2. Modify the Student Model with a Context-Aware Field:
Now, in your student.model, create a new computed field. This field’s value will depend directly on the context key we passed in the action (add_to_class_mode).
models/student_model.py:
from odoo import models, fields, api
class StudentModel(models.Model):
_name = 'student.model'
_description = 'Student Information'
name = fields.Char(string="Student Name", required=True)
# This field determines if the 'Add to Class' button should be visible
# It's not stored in the database, as its value is context-dependent.
is_add_to_class_mode = fields.Boolean(
string="Add to Class Mode",
compute='_compute_is_add_to_class_mode',
store=False # Important: Non-stored computed field
)
# The magic happens here: @api.depends_context
@api.depends_context('add_to_class_mode')
def _compute_is_add_to_class_mode(self):
"""
Computes `is_add_to_class_mode` based on the 'add_to_class_mode'
key present in the Odoo environment's context.
"""
for record in self:
# Check if 'add_to_class_mode' is True in the context
# .get() safely retrieves the value, defaulting to False if not found
record.is_add_to_class_mode = self.env.context.get('add_to_class_mode', False)
def action_link_student_to_class(self):
"""
Links the selected student to the active class.
This method is called by the dynamically appearing button.
"""
active_class_id = self.env.context.get('active_class_id')
if not active_class_id:
return {
'warning': {
'title': "Error",
'message': "No active class specified in context.",
}
}
# Find the class record and add the current student to its student_ids
class_record = self.env['class.model'].browse(active_class_id)
if class_record:
class_record.write({'student_ids': [(4, self.id)]}) # (4, ID) to link existing record
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': "Success",
'message': f"{self.name} added to {class_record.name}!",
'type': 'success',
}
}
return {}
Explanation of @api.depends_context:
@api.depends_context('add_to_class_mode'): This decorator signifies that the_compute_is_add_to_class_modemethod should be re-evaluated whenever theadd_to_class_modekey within theself.env.contextdictionary changes or is present.store=False: This is crucial for context-aware fields. Since their value is not dependent on database changes but rather on the runtime context, they are typically not stored in the database. This ensures their dynamic nature.self.env.context.get('add_to_class_mode', False): This is how you access values from the current session’s context..get()is safer than direct dictionary access ([]) as it allows for a default value if the key is not found, preventing errors.
3. Add the Conditional Button to the Student List View:
Finally, modify the student list view to include the “Add to Class” button, making its visibility dependent on our new is_add_to_class_mode field.
views/student_views.xml:
<record id="view_student_tree" model="ir.ui.view">
<field name="name">student.model.tree</field>
<field name="model">student.model</field>
<field name="arch" type="xml">
<tree string="Students">
<field name="name"/>
<!-- The 'Add to Class' button, conditionally visible -->
<button name="action_link_student_to_class"
string="Add to Class"
type="object"
icon="fa-plus-square"
attrs="{'invisible': [('is_add_to_class_mode', '=', False)]}" />
</tree>
</field>
</record>
<record id="view_student_form" model="ir.ui.view">
<field name="name">student.model.form</field>
<field name="model">student.model</field>
<field name="arch" type="xml">
<form string="Student">
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_student_model" model="ir.actions.act_window">
<field name="name">Students</field>
<field name="res_model">student.model</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_student_model" parent="menu_class_root" name="Students" action="action_student_model" sequence="20"/>
How This Transforms the User Experience:
- When you navigate to the “Students” menu item directly, the
add_to_class_modekey is not in the context. Therefore,is_add_to_class_moderemainsFalse, and the “Add to Class” button is hidden. The general student list remains clean. - When you open a “Class” record and click the “Add Students” button, the action passes
add_to_class_mode: Trueinto the context of the opened student list view. @api.depends_contextdetects this, recomputesis_add_to_class_modetoTrue, and the “Add to Class” button magically appears for each student record. This provides a seamless and guided experience for adding students to a specific class.
This example clearly demonstrates how Odoo context-aware fields allow the same view to behave differently based on the user’s workflow, eliminating the need for duplicating views.
Scenario 2: Dynamic Button Visibility Based on Current User’s Role – Teacher Actions
Another powerful application of Odoo context-aware fields is to control view elements based on the current user’s identity or role. Consider the need to show a “Special Teacher Action” button on a student’s form view, but only if the currently logged-in user is actually the teacher assigned to that student’s class.
Goal: Display a “Special Teacher Action” button on a student’s form view only if the logged-in user is the responsible teacher for that student’s class.
Step-by-Step Implementation:
1. Extend the Student Model with a Context-Aware Field for Teacher Role:
We’ll add a computed field, is_responsible_teacher, to the student.model. This field will check if the current user matches the teacher assigned to the student’s class.
models/student_model.py (continuing from previous code):
# ... (previous code for StudentModel)
class_id = fields.Many2one('class.model', string="Assigned Class") # Link to the class
is_responsible_teacher = fields.Boolean(
string="Is Responsible Teacher",
compute='_compute_is_responsible_teacher',
store=False # Not stored, context-dependent
)
# We depend on an empty context here, meaning it will recompute
# on any view load or context change, implicitly including user-related context.
# More specifically, you might depend on 'uid' if your Odoo version supports it,
# or rely on the general view load.
@api.depends_context()
def _compute_is_responsible_teacher(self):
"""
Checks if the current user is the teacher assigned to the student's class.
"""
current_user = self.env.user # Access the current logged-in user
for record in self:
is_teacher = False
if record.class_id and record.class_id.teacher_id:
# Assuming teacher_id is a Many2one to hr.employee
# and hr.employee has a user_id Many2one to res.users
if record.class_id.teacher_id.user_id and \
record.class_id.teacher_id.user_id == current_user:
is_teacher = True
record.is_responsible_teacher = is_teacher
def special_teacher_action(self):
"""
Placeholder method for the special action a teacher can perform.
"""
# Add your specific business logic here, e.g., logging grades,
# sending a notification, opening a special wizard.
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': "Teacher Action",
'message': f"Special action performed for {self.name} by {self.env.user.name}!",
'type': 'info',
}
}
Explanation:
@api.depends_context(): When no specific keys are provided, this implies that the field will be recomputed when any part of the general Odoo context changes, which effectively covers changes related to the session (like the current user upon opening a view). While Odoo doesn’t typically recompute on every single context key change without explicit dependency, opening a form view or reloading it generally triggersdepends_contextfor fields on that view. For more specific user-based recomputations, you might monitoruidoruser_idcontext keys if applicable.self.env.user: This provides a convenient way to access theres.usersrecord of the currently logged-in user.
2. Add the Conditional Button to the Student Form View:
Now, we’ll add the “Special Teacher Action” button to the student form view, controlling its visibility with the is_responsible_teacher field.
views/student_views.xml (continuing from previous code):
<record id="view_student_form" model="ir.ui.view">
<field name="name">student.model.form</field>
<field name="model">student.model</field>
<field name="arch" type="xml">
<form string="Student">
<header>
<!-- Special Teacher Action button, only visible to the responsible teacher -->
<button name="special_teacher_action"
string="Special Teacher Action"
type="object"
class="oe_highlight"
attrs="{'invisible': [('is_responsible_teacher', '=', False)]}" />
</header>
<sheet>
<group>
<field name="name"/>
<field name="class_id"/>
</group>
</sheet>
</form>
</field>
</record>
Impact on User Experience:
- When a non-assigned teacher (or any other user) views a student’s form, the
is_responsible_teacherfield computes toFalse, and the “Special Teacher Action” button remains hidden. - When the actual teacher assigned to that student’s class views the form,
is_responsible_teacherbecomesTrue, and the button appears, allowing them to perform their specific actions.
This technique is incredibly valuable for implementing granular, dynamic access control and presenting tailored interfaces based on the user’s current context and permissions. It complements traditional Odoo security mechanisms (like record rules and access rights) by offering runtime UI adjustments.
Accessing General Context Information
Beyond custom keys and user IDs, the self.env.context dictionary holds a wealth of standard Odoo context information that you can leverage with Odoo context-aware fields. This includes:
lang: The current language of the user’s session (e.g.,'en_US','fr_FR').tz: The user’s current timezone.uid: The ID of the current user.company_id: The ID of the currently active company (especially relevant in multi-company environments).active_id/active_ids/active_model: Information about the record(s) and model from which the current action was triggered.
You can print self.env.context in your Python code (e.g., using _logger.info(self.env.context)) to explore all available keys and their values during runtime.
Example: Displaying a Field Based on Language
You could use @api.depends_context('lang') to show a field with a localized warning message or alter a label based on the user’s selected language.
from odoo import models, fields, api
import logging
_logger = logging.getLogger(__name__)
class ProductTemplate(models.Model):
_inherit = 'product.template'
localized_description = fields.Char(
string="Localized Description",
compute='_compute_localized_description',
store=False
)
@api.depends_context('lang')
def _compute_localized_description(self):
"""
Computes a description based on the current user's language.
"""
lang = self.env.context.get('lang')
_logger.info(f"Computing localized description for language: {lang}")
for record in self:
if lang and lang.startswith('fr_'):
record.localized_description = f"Description en français pour {record.name}"
elif lang and lang.startswith('es_'):
record.localized_description = f"Descripción en español para {record.name}"
else:
record.localized_description = f"Default description for {record.name}"
This Odoo context-aware field can then be displayed in your product form view, showing a different description depending on the user’s interface language.
Important Considerations and Best Practices
While Odoo context-aware fields are incredibly powerful, it’s crucial to use them judiciously.
- Performance: Fields computed with
@api.depends_context( )(without specific keys) will recompute on any context change, which can happen frequently during navigation or form interaction. For complex computations, this might introduce performance overhead. Always strive to depend on specific context keys (@api.depends_context('key_name')) whenever possible to limit unnecessary recomputations. - Readability: Ensure your code remains clear. Document why a field is
store=Falseand why it relies ondepends_context. - Alternatives: Before jumping to
Odoo context-aware fields, consider if simpler alternatives are more appropriate:- Security Groups/Record Rules: For strict access control (who can see/modify what data).
- Default Values from Context: If you just need to pre-fill a field when opening a form from a specific action (e.g.,
'default_field_name': valuein context). - Client-Side JavaScript: For purely visual, non-data-dependent UI changes that can be handled directly in the browser.
- Context Key Naming: When introducing custom context keys, use descriptive and unique names to avoid conflicts with Odoo’s internal context keys.
- Testing: Thoroughly test your
Odoo context-aware fieldsunder various scenarios, including different navigation paths, user roles, and language settings, to ensure they behave as expected.
For further exploration of Odoo’s API and best practices in custom module development, consider diving into the official Odoo Developer Documentation. Additionally, mastering concepts like Odoo Custom Module Development Guide (internal link example) will greatly enhance your ability to build robust Odoo solutions.
Conclusion
Odoo context-aware fields, powered by the @api.depends_context decorator, are a game-changer for building dynamic, intuitive, and highly responsive Odoo applications. By allowing your fields to react not just to data changes but also to the broader user context, you can create flexible views that adapt to different workflows, user roles, and session specifics.
We’ve explored three powerful techniques:
- Dynamic Button Visibility based on Origin View: Streamlining workflows by showing relevant actions only when initiated from a specific source.
- Dynamic Button Visibility based on Current User’s Role: Implementing precise UI control based on who is logged in.
- Leveraging General Context Information: Adapting behavior based on language, company, or other session details.
By integrating Odoo context-aware fields into your development toolkit, you unlock a new level of sophistication and user experience in your Odoo modules. Experiment with these examples, apply them to your unique business cases, and continue to push the boundaries of what’s possible with Odoo development. If you have any questions or encounter specific challenges, feel free to share them in the comments below!
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.


Pingback: Powerful Odoo Context Tutorial: 7 Pro Steps - teguhteja.id