Перейти до основного вмісту

Python-скрипти для трансформації даних

Python-скрипти дозволяють виконувати складні трансформації даних, які неможливо реалізувати лише мапуванням полів.

Інтерфейс редактора скриптів

Python Script Editor Редактор скриптів із доступними змінними та прикладами

Посібник і довідка в інтерфейсі

Python Script Manual Вбудований посібник із переліком змінних та прикладів використання

Доступні контекстні змінні

Під час написання скриптів ви маєте доступ до таких змінних:

Основні об'єкти

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})

Найкращі практики

  1. Перевіряйте дані перед обробкою.
  2. Використовуйте try-except для контрольованих помилок.
  3. Логуйте ключові кроки трансформації.
  4. Оптимізуйте запити до БД через пакетні операції.
  5. Документуйте складну логіку коментарями.
  6. Тестуйте скрипти перед використанням у продакшені.
  7. Обробляйте None та порожні значення.
  8. Обережно конвертуйте типи даних.

Типові помилки

# ❌ Не змінюйте 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)}')

Пов’язана документація