Python-скрипти для трансформації даних
Python-скрипти дозволяють виконувати складні трансформації даних, які неможливо реалізувати лише мапуванням полів.
Інтерфейс редактора скриптів
Редактор скриптів із доступними змінними та прикладами
Посібник і довідка в інтерфейсі
Вбудований посібник із переліком змінних та прикладів використання
Доступні контекстні змінні
Під час написання скриптів ви маєте доступ до таких змінних:
Основні об'єкти
env # Середовище Odoo для доступу до БД
model # Модель Odoo, що синхронізується
records # Поточний набір записів
request_data # JSON-повідомлення від/до зовнішнього API
Службові бібліотеки
# Операції з датою/часом
time, datetime, dateutil, timezone
# Кодування
b64encode, b64decode # Base64 кодування/декодування
# Обробка помилок
UserError # Виключення з повідомленням для користувача
# Робота з записами
Command # Стандартні команди для створення/оновлення зв'язків
Режими виконання скриптів
Evaluate Mode
Повертає обчислене значення, яке буде записане в поле:
# Повернення розрахованого значення
record.price * 1.2 # Надбавка 20%
Execute Mode
Виконує складні дії без повернення результату:
# Створення пов'язаних записів
env['product.pricelist.item'].create({
'product_id': record.id,
'price': request_data.get('special_price')
})
Типові сценарії
1. Валідація даних
Перевіряйте дані перед тим, як Odoo їх обробляє:
# Перевірка формату email
import re
email = request_data.get('email', '')
if email and not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
raise UserError(f"Invalid email format: {email}")
# Контроль обов'язкових полів
required_fields = ['name', 'email', 'phone']
missing = [f for f in required_fields if not request_data.get(f)]
if missing:
raise UserError(f"Missing required fields: {', '.join(missing)}")
2. Конвертація форматів
Перетворюйте дані у формат, який очікує Odoo:
# Конвертація дати
from datetime import datetime
datetime.strptime(
request_data.get('birth_date', ''),
'%m/%d/%Y'
).strftime('%Y-%m-%d')
# Нормалізація телефону
''.join(filter(str.isdigit, request_data.get('phone', '')))
3. Умовна логіка
Застосовуйте бізнес-правила, виходячи зі значень даних:
# Категоризація клієнтів за сумою замовлень
total_orders = request_data.get('total_orders', 0)
if total_orders > 100000:
category = 'platinum'
discount = 0.20
elif total_orders > 50000:
category = 'gold'
discount = 0.15
elif total_orders > 10000:
category = 'silver'
discount = 0.10
else:
category = 'bronze'
discount = 0.05
record.write({
'customer_category': category,
'discount_percentage': discount * 100,
})
4. Робота з реляційними зв'язками
Створюйте або знаходьте пов'язані записи:
partner_data = request_data.get('company', {})
if partner_data:
partner = env['res.partner'].search([
('name', '=', partner_data.get('name')),
('is_company', '=', True)
], limit=1)
if not partner:
partner = env['res.partner'].create({
'name': partner_data.get('name'),
'is_company': True,
'email': partner_data.get('email'),
'phone': partner_data.get('phone'),
})
5. Агрегація даних
Рахуйте підсумки або формуйте нові записи на основі вхідних масивів:
line_items = request_data.get('items', [])
total = 0
tax_total = 0
for item in line_items:
quantity = item.get('quantity', 0)
price = item.get('price', 0)
tax_rate = item.get('tax_rate', 0)
subtotal = quantity * price
tax = subtotal * (tax_rate / 100)
total += subtotal
tax_total += tax
for item in line_items:
env['account.move.line'].create({
'move_id': record.id,
'product_id': item.get('product_id'),
'quantity': item.get('quantity'),
'price_unit': item.get('price'),
})
record.write({'amount': total + tax_total})
Розширені прийоми
Обробка помилок
try:
value = int(request_data.get('quantity'))
if value < 0:
raise ValueError('Quantity cannot be negative')
except ValueError as e:
env['bj.api.log'].create({
'name': f'Data transformation error: {str(e)}',
'request_data': str(request_data),
})
Логування та налагодження
import logging
_logger = logging.getLogger(__name__)
_logger.info(f'Processing record: {record.id}')
_logger.debug(f'Request data: {request_data}')
env['bj.api.log'].create({
'config_id': config.id,
'record_id': record.id,
'action': 'transform',
'details': f'Transformed {len(request_data)} fields',
})
Оптимізація продуктивності
record_ids = request_data.get('record_ids', [])
# Погано: послідовні запити до БД
# for rid in record_ids:
# env['model'].browse(rid).write({'field': value})
# Добре: пакетне оновлення
if record_ids:
env['model'].browse(record_ids).write({'field': value})
Найкращі практики
- Перевіряйте дані перед обробкою.
- Використовуйте
try-except
для контрольо ваних помилок. - Логуйте ключові кроки трансформації.
- Оптимізуйте запити до БД через пакетні операції.
- Документуйте складну логіку коментарями.
- Тестуйте скрипти перед використанням у продакшені.
- Обробляйте
None
та порожні значення. - Обережно конвертуйте типи даних.
Типові помилки
# ❌ Не змінюйте request_data напряму
request_data['field'] = 'value'
# ✅ Створюйте окремі змінні
modified_value = request_data.get('field', '') + '_suffix'
# ❌ Використання невизначених змінних
undefined_variable
# ✅ Перевіряйте наявність значень
locals().get('variable_name', default_value)
# ❌ Безкінечні цикли
while True:
process_data()
# ✅ Обмежені цикли
for i in range(max_iterations):
if condition_met:
break
process_data()
Тестування скриптів
# Тестуйте на різних наборах даних
test_cases = [
{'name': 'Test Customer', 'email': 'test@example.com'},
{'name': 'No Email'},
{'email': 'invalid-email'},
{},
]
for test_data in test_cases:
try:
result = transform_data(test_data)
print(f'Success: {result}')
except Exception as e:
print(f'Error with {test_data}: {str(e)}')