Skip to content

Métodos ORM

El método init() permite construir a nivel de modelo/base de datos. Se ejecuta una única vez durante la instalación o actualización del módulo. Definición de método:

@api.model
def init(self):
# Este código se ejecuta solo una vez durante la instalación/actualización
pass
  1. Crear registros iniciales
@api.model
def init(self):
# Crear categorías por defecto
names = ['Research', 'Development', 'Innovation']
for name in names:
category = self.env['ir.module.category'].search([('name', '=', name)], limit=1)
if not category:
self.env['ir.module.category'].create({
'name': name,
'description': f"{name} Project Category"
})
  1. Migrar datos entre versiones
@api.model
def init(self):
# Migrar datos de una versión anterior
old_records = self.env['old.model'].search([])
for old in old_records:
self.create({
'name': old.name,
'value': old.old_value * 2 # Transformación de datos
})
  1. Inicializar datos técnicos
@api.model
def init(self):
# Crear secuencias automáticas
if not self.env['ir.sequence'].search([('code', '=', 'my.sequence')]):
self.env['ir.sequence'].create({
'name': 'Secuencia para Mi Modelo',
'code': 'my.sequence',
'prefix': 'MOD-',
'padding': 6,
'number_next': 1
})
  1. Validar e inicializar configuraciones
@api.model
def init(self):
# Verificar y crear configuración por defecto
config = self.env['my.config'].search([], limit=1)
if not config:
self.env['my.config'].create({
'default_value': 100,
'max_limit': 1000,
'auto_approve': True
})
  1. Usar SUPERUSER_ID para operaciones críticas
@api.model
def init(self):
env = api.Environment(self.env.cr, SUPERUSER_ID, self.env.context)
# Operaciones con permisos de superusuario
  1. Verificar si ya se ejecutó
@api.model
def init(self):
# Verificar si ya existe un registro de inicialización
initialized = self.env['ir.config_parameter'].get_param('my_module.initialized')
if not initialized:
# Código de inicialización
self.env['ir.config_parameter'].set_param('my_module.initialized', True)
  1. Manejar errores adecuadamente
@api.model
def init(self):
try:
# Código de inicialización
self._initialize_data()
except Exception as e:
# Log del error pero no detener la instalación
_logger.error("Error en init(): %s", str(e))
  1. Para migraciones complejas
@api.model
def init(self):
# Verificar la versión anterior del módulo
current_version = self.env['ir.module.module'].search([
('name', '=', 'my_module')
]).installed_version
if current_version == '1.0':
self._migrate_from_v1_to_v2()
elif current_version == '2.0':
self._migrate_from_v2_to_v3()

El método search() permite buscar y filtrar registros en la base de datos basándose en criterios específicos.

Definición de método:

records = self.env["MODEL_NAME"].search(domain, offset=0, limit=None, order=None)
  • domain (dominio) Lista de tuplas que define los criterios de búsqueda.
# Buscar productos activos con precio mayor a 100
domain = [('active', '=', True), ('price', '>', 100)]
products = self.env['product.product'].search(domain)
  • offset (desplazamiento) Número de registros a saltar desde el inicio.
# Saltar los primeros 10 registros
records = self.env['model.name'].search([], offset=10)
  • limit (límite) Número máximo de registros a retornar.
# Obtener solo 5 registros
records = self.env['model.name'].search([], limit=5)
  • order (ordenamiento) Cadena que especifica el orden de los resultados.
# Ordenar por nombre ascendente y fecha descendente
records = self.env['model.name'].search([], order='name asc, date desc')
# Operadores de comparación
[('campo', '=', valor)] # Igual
[('campo', '!=', valor)] # Diferente
[('campo', '<', valor)] # Menor que
[('campo', '<=', valor)] # Menor o igual
[('campo', '>', valor)] # Mayor que
[('campo', '>=', valor)] # Mayor o igual
[('campo', 'like', 'patrón')] # Coincide con patrón (usar %)
[('campo', 'ilike', 'patrón')] # Coincide sin importar mayúsculas
[('campo', 'in', [lista])] # En una lista de valores
[('campo', 'not in', [lista])] # No en una lista
[('campo', 'child_of', id)] # Hijos de un registro
# Operadores lógicos
['&', (cond1), (cond2)] # AND (y)
['|', (cond1), (cond2)] # OR (o)
['!', (cond1)] # NOT (no)
  1. Búsqueda simple
# Todos los usuarios activos
users = self.env['res.users'].search([('active', '=', True)])
# Productos con stock mayor a 0
products = self.env['product.product'].search([('qty_available', '>', 0)])
  1. Búsqueda con múltiples condiciones
# Usuarios activos del grupo "Ventas"
domain = [
('active', '=', True),
('groups_id', 'in', [self.env.ref('sales_team.group_sale_salesman').id])
]
sales_users = self.env['res.users'].search(domain)
  1. Búsqueda con operadores lógicos
# Productos activos O con stock > 100
domain = [
'|',
('active', '=', True),
('qty_available', '>', 100)
]
products = self.env['product.product'].search(domain)
  1. Búsqueda con ordenamiento y límite
# Últimos 10 pedidos de venta ordenados por fecha
orders = self.env['sale.order'].search(
[],
order='date_order desc',
limit=10
)
  1. Búsqueda de registros relacionados
# Todas las órdenes de venta de un cliente específico
customer = self.env['res.partner'].search([('name', '=', 'Cliente Ejemplo')], limit=1)
if customer:
orders = self.env['sale.order'].search([('partner_id', '=', customer.id)])
  1. Sobre-escritura del método base
@api.model
def search(self, domain, offset=0, limit=None, order=None, count=False):
# Modificar el dominio antes de la búsqueda
domain = self._modify_search_domain(domain)
# Ejecutar la búsqueda original
result = super(MyModel, self).search(
domain, offset=offset, limit=limit, order=order, count=count
)
# Filtrar resultados después de la búsqueda (si es necesario)
result = self._filter_search_results(result)
return result
  1. Usar limit=1 cuando se espera un solo resultado
user = self.env['res.users'].search([('login', '=', 'admin')], limit=1)
  1. Evitar búsquedas sin dominio o limite en modelos grandes
some_partners = self.env['res.partner'].search([], limit=100)
  1. Usar índices en campos de búsqueda frecuente
class MyModel(models.Model):
_name = 'my.model'
name = fields.Char(string='Nombre', index=True)
  1. Combinar con otras métodos del ORM
# Buscar y luego actualizar
partners = self.env['res.partner'].search([('country_id', '=', False)])
partners.write({'country_id': self.env.ref('base.us').id})

El método create() permite crear nuevos registros en la base de datos. Es la operación “Create” del CRUD.

Definición de método:

record = self.env['MODEL_NAME'].create(vals)
  • vals (valores) Diccionario que contiene los valores para los campos del nuevo registro.
vals = {
'name': 'NAME',
'field1': 'value1',
'field2': 'value2',
# ...
}
new_record = self.env['MODEL_NAME'].create(vals)
  1. Creación simple
# Crear un nuevo partner
partner_vals = {
'name': 'Juan Pérez',
'email': 'juan@example.com',
'phone': '+1234567890',
'company_type': 'person'
}
new_partner = self.env['res.partner'].create(partner_vals)
  1. Creación con campos relacionales
# Crear un usuario con grupos
user_vals = {
'name': 'Maria García',
'login': 'maria@company.com',
'groups_id': [(6, 0, [ # Asignar grupos existentes
self.env.ref('base.group_user').id,
self.env.ref('sales_team.group_sale_salesman').id
])]
}
new_user = self.env['res.users'].create(user_vals)
  1. Creación con campos computed y related
# Crear una orden de venta
sale_vals = {
'partner_id': self.env.ref('base.res_partner_1').id,
'order_line': [(0, 0, { # Crear línea de orden
'product_id': self.env.ref('product.product_product_1').id,
'product_uom_qty': 5,
'price_unit': 100
})]
}
new_order = self.env['sale.order'].create(sale_vals)
  1. Sobre-escritura del método base
@api.model_create_multi
def create(self, vals_list):
# Lógica antes de crear
for vals in vals_list:
if "name" in vals:
vals["name"] = vals[
"name"
].upper() # Ejemplo: convertir nombre a mayúsculas
# Llamar al create original
records = super(MyModel, self).create(vals_list)
# Lógica después de crear
for record in records:
record._some_post_creation_action()
return records
  1. Usar diccionarios completos
# Usar diccionarios completos
vals = {
'name': 'Nombre completo',
'field1': 'valor1',
'field2': 'valor2'
}
record = self.env['model.name'].create(vals)
  1. Validar datos antes de crear
def create_safe_record(self, vals):
"""Crear registro con validación"""
# Validar campos requeridos
required_fields = ['name', 'code']
for field in required_fields:
if field not in vals:
raise UserError(f"El campo {field} es requerido")
# Crear registro
return self.env['model.name'].create(vals)

El método write() permite actualizar registros existentes en la base de datos. Es la operación “Update” del CRUD.

Definición de método:

# Actualizar uno o múltiples registros
records.write(vals)
  • vals (valores) Diccionario que contiene los valores para los campos del nuevo registro.
vals = {
'name': 'NEW_NAME',
'field1': 'new_value1',
'field2': 'new_value2',
# ...
}
records.write(vals)
  1. Actualización simple
# Actualizar un registro específico
partner = self.env['res.partner'].browse(1)
partner.write({'name': 'Nuevo nombre', 'email': 'nuevo@email.com'})
# Actualizar múltiples registros
partners = self.env['res.partner'].search([('country_id', '=', False)])
partners.write({'country_id': self.env.ref('base.us').id})
  1. Actualización con campos relacionales
# Actualizar relaciones Many2many
user = self.env['res.users'].browse(1)
user.write({
'groups_id': [(6, 0, [ # Reemplazar grupos existentes
self.env.ref('base.group_user').id,
self.env.ref('sales_team.group_sale_salesman').id
])]
})
# Agregar a relaciones existentes
user.write({
'groups_id': [(4, self.env.ref('account.group_account_user').id)]
})
  1. Actualización con operaciones en campos
# Incrementar un valor numérico
product = self.env['product.product'].browse(1)
product.write({'qty_available': product.qty_available + 10})
# Concatenar texto
partner.write({'name': partner.name + " - Actualizado"})
  1. Sobre-escritura del método base
def write(self, vals):
# Lógica antes de escribir
if "state" in vals and vals["state"] == "done":
vals["done_date"] = fields.Date.today()
# Llamar al write original
result = super(MiModelo, self).write(vals)
# Lógica después de escribir
if "amount" in vals:
self._recompute_totals()
return result
  1. Actualizar múltiples registros a la vez
# Actualizar múltiples registros
records = self.env['model.name'].search([('state', '=', 'draft')])
records.write({'state': 'confirmed'})
# Usar operaciones relacionales eficientes
partner.write({'category_id': [(4, new_category_id)]})
  1. Validar antes de actualizar
def safe_write(self, records, vals):
"""Escribir con validación adicional"""
# Validar permisos
if not self.env.user.has_group('base.group_system'):
raise AccessError("No tiene permisos para actualizar")
# Validar datos
if 'sensitive_field' in vals and not self._validate_sensitive_data(vals['sensitive_field']):
raise ValidationError("Dato sensible inválido")
# Ejecutar write
return records.write(vals)

El método unlink() permite eliminar registros permanentemente de la base de datos. Es la operación “Delete” del CRUD.

Definición de método:

# Eliminar uno o múltiples registros
records.unlink()
  1. Eliminación básica
# Eliminar un registro específico
partner = self.env['res.partner'].browse(1)
partner.unlink() # Elimina el partner con ID 1
# Eliminar múltiples registros
partners = self.env['res.partner'].search([('active', '=', False)])
partners.unlink() # Elimina todos los partners inactivos
  1. Eliminación con verificación
# Verificar existencia antes de eliminar
record = self.env['model.name'].browse(999)
if record.exists():
record.unlink()
_logger.info("Registro eliminado")
else:
_logger.warning("El registro no existe")
  1. Eliminación condicional
# Eliminar solo registros que cumplan cierta condición
old_orders = self.env['sale.order'].search([
('state', '=', 'cancel'),
('create_date', '<', '2023-01-01')
])
for order in old_orders:
if order.can_be_deleted(): # Método personalizado de validación
order.unlink()
  1. Sobre-escritura del método base
def unlink(self):
# Logica antes de eliminar
for partner in self:
if partner.sale_order_ids:
raise UserError(
(
"No puedes eliminar el partner %s porque tiene órdenes de venta asociadas. "
"Archívalo en lugar de eliminarlo."
)
% partner.name
)
if partner.invoice_ids:
raise UserError(
(
"No puedes eliminar el partner %s porque tiene facturas asociadas. "
"Archívalo en lugar de eliminarlo."
)
% partner.name
)
# Ejecutar la eliminación
return super(CustomPartner, self).unlink()
  1. Verificar existencia antes de eliminar
# Verificar existencia antes de eliminar
if record.exists():
record.unlink()
# Usar unlink() en conjunto de registros
records = self.env['model'].search([('active', '=', False)])
records.unlink() # ✅ Una sola operación de base de datos
  1. Eliminación segura
def safe_delete(self, record_ids):
"""Eliminación segura con validación"""
records = self.env['model.name'].browse(record_ids).exists()
if not records:
return {"success": False, "message": "Registros no encontrados"}
try:
records.unlink()
return {"success": True, "message": f"{len(records)} registros eliminados"}
except Exception as e:
return {"success": False, "message": f"Error: {str(e)}"}