# QWeb Template Best Practices - Odoo 18 **Reference**: website_sale_aplicoop template error fix **Odoo Version**: 18.0+ **Created**: 2026-02-16 --- ## Table of Contents 1. [Attribute Expression Best Practices](#attribute-expression-best-practices) 2. [None/Null Safety Patterns](#nonenull-safety-patterns) 3. [Variable Computation Patterns](#variable-computation-patterns) 4. [Common Pitfalls](#common-pitfalls) 5. [Real-World Examples](#real-world-examples) --- ## Attribute Expression Best Practices ### The Problem: t-attf-* Operator Issues **Issue**: QWeb's `t-attf-*` (template attribute) directives don't handle chained `or` operators well when expressions can evaluate to None. ```xml
``` ### The Solution: Pre-compute Safe Variables **Key Pattern**: Use `` to compute safe values **before** using them in attributes. ```xml ``` ### Why This Works 1. **Separation of Concerns**: Logic (t-set) is separate from rendering (t-attf-*) 2. **Explicit Evaluation**: QWeb evaluates the conditional expression fully before passing to t-set 3. **Type Safety**: Pre-computed value is guaranteed to be non-None 4. **Readability**: Clear intent of what value is being used --- ## None/Null Safety Patterns ### Pattern 1: Simple Fallback **Scenario**: Value might be None, need a default ```python # Python context display_price = None product_price = 100.0 ``` ```xml
``` ### Pattern 2: Nested Object Access **Scenario**: Need to access nested attributes safely (e.g., `product.uom_id.category_id.name`) ```python # Python context product.uom_id = UoM(...) # Valid UoM with category_id product.uom_id.category_id = None # Category is None ``` ```xml
``` ### Pattern 3: Type Coercion **Scenario**: Value might be wrong type, need guaranteed type ```python # Python context quantity = "invalid_string" # Should be int/float ``` ```xml ``` --- ## Variable Computation Patterns ### Pattern 1: Sequential Computation When multiple safe variables depend on each other: ```xml ``` ### Pattern 2: Conditional Blocks with t-set When logic is complex, use `t-if` with `t-set`: ```xml
``` ### Pattern 3: Python Expressions vs. Template Expressions ```xml
``` --- ## Common Pitfalls ### Pitfall 1: Trusting `or` in Attributes **Problem**: The `or` operator in template attributes doesn't work like Python's `or` ```xml
``` **Why**: QWeb's attribute template system has special parsing rules that don't work well with complex expressions. ### Pitfall 2: Chained Attribute Access Without Null-Checking **Problem**: Assuming nested attributes exist ```python # Context: product.uom_id might be None ``` ```xml
``` ### Pitfall 3: Complex Logic in t-att (non-template attributes) **Problem**: Using complex expressions in non-template attributes ```xml
``` ### Pitfall 4: Forgetting t-attf- Prefix **Problem**: Using `data-*` instead of `t-attf-data-*` ```xml ``` --- ## Real-World Examples ### Example 1: E-commerce Product Card **Scenario**: Displaying product with optional fields ```xml ``` ### Example 2: Nested Data Attributes **Scenario**: Form with deeply nested object access ```xml
...
``` ### Example 3: Conditional Styling **Scenario**: Attribute value depends on conditions ```xml
...
``` --- ## Summary Table | Pattern | ❌ Don't | ✅ Do | |---------|---------|-------| | **Fallback values** | `t-attf-x="{{ a or b or c }}"` | `` then `{{ x }}` | | **Nested objects** | `{{ obj.nested.prop }}` | `` | | **Type checking** | `` | `` | | **Complex logic** | `{{ function(a, b) if condition else default }}` | Pre-compute in Python, reference in template | | **Chained operators** | `{{ a or b if c else d or e }}` | Break into multiple t-set statements | --- ## Tools & Validation ### XML Validation ```bash # Validate XML syntax python3 -m xml.dom.minidom template.xml # Or use pre-commit hooks pre-commit run check-xml ``` ### QWeb Template Testing ```python # In Odoo shell from odoo.tools import misc arch = env['ir.ui.view'].search([('name', '=', 'template_name')])[0].arch # Check if template compiles without errors ``` ### Debugging Template Issues ```xml ``` --- ## References - [Odoo QWeb Documentation](https://www.odoo.com/documentation/18.0/developer/reference/frontend/qweb.html) - [Odoo Templates](https://www.odoo.com/documentation/18.0/developer/reference/backend/orm.html#templates) - [Python Ternary Expressions](https://docs.python.org/3/tutorial/controlflow.html#more-on-conditions) --- ## Related Issues & Fixes - [website_sale_aplicoop Template Error Fix](./FIX_TEMPLATE_ERROR_SUMMARY.md) - Real-world example of this pattern - [Git Commit 0a0cf5a](../../../.git/logs/HEAD) - Implementation of these patterns --- **Last Updated**: 2026-02-16 **Odoo Version**: 18.0+ **Status**: ✅ Documented and tested