Migration from odoorpc¶
This guide helps you migrate from odoorpc to Zenoo RPC, highlighting the key differences and providing step-by-step migration instructions.
Why Migrate?¶
| Feature | odoorpc | Zenoo RPC |
|---|---|---|
| Async Support | ❌ Synchronous only | ✅ Async-first with asyncio |
| Type Safety | ❌ Raw dicts/lists | ✅ Pydantic models with validation |
| Query Builder | ❌ Manual domain building | ✅ Fluent, chainable queries |
| Caching | ❌ No built-in caching | ✅ Intelligent TTL/LRU caching |
| Transactions | ❌ No transaction support | ✅ ACID transactions with rollback |
| Batch Operations | ❌ Manual batching | ✅ Built-in bulk operations |
| Error Handling | ❌ Generic exceptions | ✅ Structured exception hierarchy |
| Performance | ❌ Multiple RPC calls | ✅ Optimized single calls |
| IDE Support | ❌ No autocomplete | ✅ Full IntelliSense support |
Quick Comparison¶
Connection & Authentication¶
odoorpc:
import odoorpc
# Connect
odoo = odoorpc.ODOO('localhost', port=8069)
# Login
odoo.login('demo', 'admin', 'admin')
# Access models
Partner = odoo.env['res.partner']
Zenoo RPC:
import asyncio
from zenoo_rpc import ZenooClient
from zenoo_rpc.models.common import ResPartner
async def main():
# Connect with context manager
async with ZenooClient("localhost", port=8069) as client:
# Login
await client.login("demo", "admin", "admin")
# Access models (type-safe)
partners = client.model(ResPartner)
asyncio.run(main())
Basic Queries¶
odoorpc:
# Search and browse (2 RPC calls)
partner_ids = Partner.search([('is_company', '=', True)], limit=10)
partners = Partner.browse(partner_ids)
# Access data
for partner in partners:
print(partner.name) # May trigger additional RPC calls
Zenoo RPC:
# Single RPC call with type safety
partners = await client.model(ResPartner).filter(
is_company=True
).limit(10).all()
# Type-safe access
for partner in partners:
print(partner.name) # No additional RPC calls
Step-by-Step Migration¶
1. Update Dependencies¶
Replace in your requirements.txt or pyproject.toml:
2. Convert Connection Code¶
Before (odoorpc):
import odoorpc
def connect_odoo():
odoo = odoorpc.ODOO('localhost', port=8069)
odoo.login('demo', 'admin', 'admin')
return odoo
odoo = connect_odoo()
After (Zenoo RPC):
import asyncio
from zenoo_rpc import ZenooClient
async def connect_odoo():
client = ZenooClient("localhost", port=8069)
await client.login("demo", "admin", "admin")
return client
# Use with context manager for automatic cleanup
async def main():
async with ZenooClient("localhost", port=8069) as client:
await client.login("demo", "admin", "admin")
# Your code here
3. Convert Model Access¶
Before (odoorpc):
After (Zenoo RPC):
from zenoo_rpc.models.common import ResPartner, ProductProduct
# Type-safe model access
partners = client.model(ResPartner)
products = client.model(ProductProduct)
4. Convert Search Operations¶
Before (odoorpc):
# Manual domain building
partner_ids = Partner.search([
('is_company', '=', True),
('name', 'ilike', 'acme%')
], limit=10, offset=20)
partners = Partner.browse(partner_ids)
After (Zenoo RPC):
# Fluent query builder
partners = await client.model(ResPartner).filter(
is_company=True,
name__ilike="acme%"
).limit(10).offset(20).all()
5. Convert CRUD Operations¶
Create Records¶
Before (odoorpc):
partner_id = Partner.create({
'name': 'New Company',
'is_company': True,
'email': 'contact@company.com'
})
partner = Partner.browse(partner_id)
After (Zenoo RPC):
partner = await client.model(ResPartner).create({
"name": "New Company",
"is_company": True,
"email": "contact@company.com"
})
Update Records¶
Before (odoorpc):
After (Zenoo RPC):
Delete Records¶
Before (odoorpc):
After (Zenoo RPC):
6. Convert Complex Queries¶
Before (odoorpc):
# Complex domain with OR conditions
domain = [
'|',
('name', 'ilike', 'acme%'),
('email', 'ilike', '%acme%'),
('is_company', '=', True)
]
partner_ids = Partner.search(domain)
After (Zenoo RPC):
from zenoo_rpc import Q
partners = await client.model(ResPartner).filter(
Q(name__ilike="acme%") | Q(email__ilike="%acme%"),
is_company=True
).all()
7. Convert Relationship Access¶
Before (odoorpc):
partner = Partner.browse(1)
country_id = partner.country_id.id
country_name = partner.country_id.name # Additional RPC call
After (Zenoo RPC):
partner = await client.model(ResPartner).get(1)
# Lazy loading - only loads when accessed
if partner.country_id:
country = await partner.country_id
print(country.name)
Advanced Migration Patterns¶
Batch Operations¶
Before (odoorpc):
# Manual batching
data = [
{'name': 'Company 1', 'is_company': True},
{'name': 'Company 2', 'is_company': True},
{'name': 'Company 3', 'is_company': True},
]
partner_ids = []
for item in data:
partner_id = Partner.create(item)
partner_ids.append(partner_id)
After (Zenoo RPC):
# Built-in batch operations
data = [
{"name": "Company 1", "is_company": True},
{"name": "Company 2", "is_company": True},
{"name": "Company 3", "is_company": True},
]
partners = await client.model(ResPartner).bulk_create(data)
Error Handling¶
Before (odoorpc):
try:
partner = Partner.browse(1)
partner.write({'email': 'invalid-email'})
except Exception as e:
print(f"Error: {e}")
After (Zenoo RPC):
from zenoo_rpc.exceptions import ValidationError, AccessError
try:
await client.model(ResPartner).update(1, {
"email": "invalid-email"
})
except ValidationError as e:
print(f"Validation error: {e}")
except AccessError as e:
print(f"Access denied: {e}")
Caching¶
Before (odoorpc):
# Manual caching
_cache = {}
def get_partners():
if 'partners' not in _cache:
partner_ids = Partner.search([('is_company', '=', True)])
_cache['partners'] = Partner.browse(partner_ids)
return _cache['partners']
After (Zenoo RPC):
# Built-in intelligent caching
async with ZenooClient("localhost", port=8069) as client:
# Setup caching
await client.cache_manager.setup_memory_cache(
max_size=1000,
default_ttl=300
)
# Automatic caching
partners = await client.model(ResPartner).filter(
is_company=True
).all() # Cached automatically
Migration Checklist¶
Phase 1: Preparation¶
- Install Zenoo RPC:
pip install zenoo-rpc - Review current odoorpc usage patterns
- Identify async/await conversion points
- Plan testing strategy
Phase 2: Core Migration¶
- Convert connection and authentication code
- Migrate model access to type-safe models
- Convert search operations to query builder
- Update CRUD operations
- Migrate relationship access patterns
Phase 3: Advanced Features¶
- Implement proper error handling
- Add caching where beneficial
- Convert to batch operations where applicable
- Add transaction support for data integrity
- Implement retry mechanisms for resilience
Phase 4: Testing & Optimization¶
- Add comprehensive tests
- Performance testing and optimization
- Monitor and tune caching
- Review and optimize query patterns
Common Migration Challenges¶
1. Async/Await Conversion¶
Challenge: Converting synchronous code to async
Solution: Use asyncio.run() for entry points and await for all Zenoo RPC calls
2. Type Safety Adoption¶
Challenge: Moving from dynamic dicts to typed models Solution: Start with common models, gradually add custom models
3. Query Builder Learning¶
Challenge: Learning new query syntax Solution: Use the migration examples above, refer to Django ORM patterns
4. Error Handling Updates¶
Challenge: Different exception hierarchy Solution: Catch specific Zenoo RPC exceptions, use structured error handling
Performance Improvements¶
After migration, you should see:
- 50-80% fewer RPC calls due to optimized queries
- Better response times with intelligent caching
- Improved reliability with retry mechanisms
- Better maintainability with type safety
Getting Help¶
If you encounter issues during migration:
- Check the Troubleshooting Guide
- Review API Reference for detailed documentation
- Ask questions in GitHub Discussions
- Report bugs in GitHub Issues
Next Steps¶
After migration:
- User Guide - Learn advanced features
- Performance Optimization - Optimize your code
- Testing Strategies - Test your migrated code
- Production Deployment - Deploy with confidence