[DOC] Update template error documentation with final solution
This commit is contained in:
parent
5721687488
commit
f2a8596d75
2 changed files with 347 additions and 28 deletions
|
|
@ -1,7 +1,8 @@
|
|||
# Fix Template Error Summary - website_sale_aplicoop
|
||||
|
||||
**Date**: 2026-02-16
|
||||
**Status**: ✅ RESOLVED
|
||||
**Final Status**: ✅ PERMANENTLY RESOLVED
|
||||
**Solution Commit**: 5721687
|
||||
**Version**: 18.0.1.1.1
|
||||
|
||||
---
|
||||
|
|
@ -10,45 +11,120 @@
|
|||
|
||||
The `eskaera_shop_products` QWeb template was throwing a `TypeError: 'NoneType' object is not callable` error when loading the store page.
|
||||
|
||||
### Root Cause
|
||||
### Root Cause - QWeb Parsing Limitations
|
||||
|
||||
QWeb templates don't handle the `or` operator reliably when used directly in `t-attf-*` (attribute) expressions, especially when values can be `None`.
|
||||
QWeb has strict limitations on what expressions it can parse:
|
||||
|
||||
**Original problematic code**:
|
||||
```xml
|
||||
<form ...
|
||||
t-attf-data-product-price="{{ display_price or product.list_price or 0 }}"
|
||||
t-attf-data-uom-category="{{ product.uom_id.category_id.name if product.uom_id.category_id else '' }}"
|
||||
>
|
||||
```
|
||||
1. **Complex nested conditionals in t-set fail**
|
||||
```xml
|
||||
❌ <t t-set="x" t-value="a if a else (b if b else c)"/>
|
||||
```
|
||||
|
||||
When `display_price` was `None`, QWeb would try to evaluate the `or` operator incorrectly, causing the error.
|
||||
2. **Direct 'or' in attributes unreliable**
|
||||
```xml
|
||||
❌ <div t-attf-val="{{ price or fallback }}"/>
|
||||
```
|
||||
|
||||
3. **Deep object chains with conditionals fail**
|
||||
```xml
|
||||
❌ t-set="uom" t-value="product.uom_id.category_id.name if product.uom_id.category_id else ''"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Solution
|
||||
|
||||
### Pattern: Intermediate Variable + Simple Fallback
|
||||
### Architecture: Move Logic to Controller
|
||||
|
||||
The key insight is that complex conditional expressions in `t-set` can fail. Instead,
|
||||
use Python's native `or` operator with intermediate variables to handle None values safely.
|
||||
**Final insight**: Don't fight QWeb's limitations. Move ALL complex logic to the Python controller where it belongs.
|
||||
|
||||
**Fixed code**:
|
||||
```xml
|
||||
<!-- Step 1: Extract the price value from price_info -->
|
||||
<t t-set="display_price_value"
|
||||
t-value="price_info.get('price')"/>
|
||||
#### The Pattern
|
||||
|
||||
<!-- Step 2: Use Python's 'or' operator for safe fallback -->
|
||||
<t t-set="display_price"
|
||||
t-value="display_price_value or product.list_price or 0.0"/>
|
||||
|
||||
<!-- Step 3: Reference the computed variable in attributes -->
|
||||
<form ...
|
||||
t-attf-data-product-price="{{ display_price }}"
|
||||
t-attf-data-uom-category="{{ safe_uom_category }}"
|
||||
>
|
||||
```
|
||||
CONTROLLER (Python)
|
||||
↓ (process data, handle None)
|
||||
product_display_info = {
|
||||
product.id: {
|
||||
'display_price': 10.99, # Always a float, never None
|
||||
'safe_uom_category': 'Weight' # Always a string, never None
|
||||
}
|
||||
}
|
||||
↓ (pass clean data to template)
|
||||
TEMPLATE (QWeb)
|
||||
↓ (simple dict.get() calls, no logic)
|
||||
<form t-attf-data-price="{{ product_display_info.get(product.id, {}).get('display_price', 0.0) }}"/>
|
||||
```
|
||||
|
||||
#### Implementation
|
||||
|
||||
**In Controller** - Added `_prepare_product_display_info()` method:
|
||||
|
||||
```python
|
||||
def _prepare_product_display_info(self, product, product_price_info):
|
||||
"""Pre-process all display values for QWeb safety.
|
||||
|
||||
All logic happens HERE in Python, not in template.
|
||||
Returns dict with safe values ready for display.
|
||||
"""
|
||||
# Get price - handle None safely
|
||||
price_data = product_price_info.get(product.id, {})
|
||||
price = price_data.get("price", product.list_price) if price_data else product.list_price
|
||||
price_safe = float(price) if price else 0.0
|
||||
|
||||
# Get UoM category - handle None/nested attributes safely
|
||||
uom_category_name = ""
|
||||
if product.uom_id:
|
||||
if product.uom_id.category_id:
|
||||
uom_category_name = product.uom_id.category_id.name or ""
|
||||
|
||||
return {
|
||||
"display_price": price_safe, # Never None
|
||||
"safe_uom_category": uom_category_name, # Never None
|
||||
}
|
||||
```
|
||||
|
||||
**In Template** - Simple dict.get() calls:
|
||||
|
||||
```xml
|
||||
<!-- Just retrieve pre-processed values -->
|
||||
<t t-set="display_price"
|
||||
t-value="product_display_info.get(product.id, {}).get('display_price', 0.0)"/>
|
||||
|
||||
<t t-set="safe_uom_category"
|
||||
t-value="product_display_info.get(product.id, {}).get('safe_uom_category', '')"/>
|
||||
|
||||
<!-- Use simple variable references -->
|
||||
<form t-attf-data-product-price="{{ display_price }}"
|
||||
t-attf-data-uom-category="{{ safe_uom_category }}"/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Changed
|
||||
|
||||
### Files Modified
|
||||
|
||||
1. **website_sale_aplicoop/controllers/website_sale.py**
|
||||
- Added `_prepare_product_display_info()` method (lines 390-417)
|
||||
- Generate `product_display_info` dict in `eskaera_shop()` (lines 1062-1065)
|
||||
- Generate `product_display_info` dict in `load_eskaera_page()` (lines 1260-1263)
|
||||
- Pass to template renders
|
||||
|
||||
2. **website_sale_aplicoop/views/website_templates.xml**
|
||||
- Removed complex conditional expressions from template
|
||||
- Replaced with simple `dict.get()` calls
|
||||
- No business logic remains in template
|
||||
|
||||
### Iteration History
|
||||
|
||||
| Commit | Approach | Result |
|
||||
|--------|----------|--------|
|
||||
| df57233 | Add `or` operators in attributes | ❌ Error persisted |
|
||||
| 0a0cf5a | Complex nested conditionals in t-set | ❌ Error persisted |
|
||||
| 8e5a4a3 | Three-step pattern with `or` chains | ⚠️ Error persisted |
|
||||
| 5721687 | Move logic to controller | ✅ SOLVED |
|
||||
|
||||
|
||||
|
||||
### Why This Works
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue