Métodos ORM
Método init()
Section titled “Método init()”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.modeldef init(self): # Este código se ejecuta solo una vez durante la instalación/actualización passEjemplos
Section titled “Ejemplos”- Crear registros iniciales
@api.modeldef 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" })- Migrar datos entre versiones
@api.modeldef 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 })- Inicializar datos técnicos
@api.modeldef 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 })- Validar e inicializar configuraciones
@api.modeldef 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 })Buenas Prácticas
Section titled “Buenas Prácticas”- Usar SUPERUSER_ID para operaciones críticas
@api.modeldef init(self): env = api.Environment(self.env.cr, SUPERUSER_ID, self.env.context) # Operaciones con permisos de superusuario- Verificar si ya se ejecutó
@api.modeldef 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)- Manejar errores adecuadamente
@api.modeldef 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))- Para migraciones complejas
@api.modeldef 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()Método search()
Section titled “Método search()”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)Parámetros
Section titled “Parámetros”- domain (dominio) Lista de tuplas que define los criterios de búsqueda.
# Buscar productos activos con precio mayor a 100domain = [('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 registrosrecords = self.env['model.name'].search([], offset=10)- limit (límite) Número máximo de registros a retornar.
# Obtener solo 5 registrosrecords = self.env['model.name'].search([], limit=5)- order (ordenamiento) Cadena que especifica el orden de los resultados.
# Ordenar por nombre ascendente y fecha descendenterecords = self.env['model.name'].search([], order='name asc, date desc')Operadores en dominios
Section titled “Operadores en dominios”# 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)Ejemplos
Section titled “Ejemplos”- Búsqueda simple
# Todos los usuarios activosusers = self.env['res.users'].search([('active', '=', True)])
# Productos con stock mayor a 0products = self.env['product.product'].search([('qty_available', '>', 0)])- 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)- Búsqueda con operadores lógicos
# Productos activos O con stock > 100domain = [ '|', ('active', '=', True), ('qty_available', '>', 100)]products = self.env['product.product'].search(domain)- Búsqueda con ordenamiento y límite
# Últimos 10 pedidos de venta ordenados por fechaorders = self.env['sale.order'].search( [], order='date_order desc', limit=10)- Búsqueda de registros relacionados
# Todas las órdenes de venta de un cliente específicocustomer = self.env['res.partner'].search([('name', '=', 'Cliente Ejemplo')], limit=1)if customer: orders = self.env['sale.order'].search([('partner_id', '=', customer.id)])- Sobre-escritura del método base
@api.modeldef 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 resultBuenas Prácticas
Section titled “Buenas Prácticas”- Usar limit=1 cuando se espera un solo resultado
user = self.env['res.users'].search([('login', '=', 'admin')], limit=1)- Evitar búsquedas sin dominio o limite en modelos grandes
some_partners = self.env['res.partner'].search([], limit=100)- Usar índices en campos de búsqueda frecuente
class MyModel(models.Model): _name = 'my.model'
name = fields.Char(string='Nombre', index=True)- Combinar con otras métodos del ORM
# Buscar y luego actualizarpartners = self.env['res.partner'].search([('country_id', '=', False)])partners.write({'country_id': self.env.ref('base.us').id})Método create()
Section titled “Método create()”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)Parámetros
Section titled “Parámetros”- 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)Ejemplos
Section titled “Ejemplos”- Creación simple
# Crear un nuevo partnerpartner_vals = { 'name': 'Juan Pérez', 'email': 'juan@example.com', 'phone': '+1234567890', 'company_type': 'person'}new_partner = self.env['res.partner'].create(partner_vals)- Creación con campos relacionales
# Crear un usuario con gruposuser_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)- Creación con campos computed y related
# Crear una orden de ventasale_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)- 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 recordsBuenas Prácticas
Section titled “Buenas Prácticas”- Usar diccionarios completos
# Usar diccionarios completosvals = { 'name': 'Nombre completo', 'field1': 'valor1', 'field2': 'valor2'}record = self.env['model.name'].create(vals)- 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)Método write()
Section titled “Método write()”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 registrosrecords.write(vals)Parámetros
Section titled “Parámetros”- 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)Ejemplos
Section titled “Ejemplos”- Actualización simple
# Actualizar un registro específicopartner = self.env['res.partner'].browse(1)partner.write({'name': 'Nuevo nombre', 'email': 'nuevo@email.com'})
# Actualizar múltiples registrospartners = self.env['res.partner'].search([('country_id', '=', False)])partners.write({'country_id': self.env.ref('base.us').id})- Actualización con campos relacionales
# Actualizar relaciones Many2manyuser = 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 existentesuser.write({ 'groups_id': [(4, self.env.ref('account.group_account_user').id)]})- Actualización con operaciones en campos
# Incrementar un valor numéricoproduct = self.env['product.product'].browse(1)product.write({'qty_available': product.qty_available + 10})
# Concatenar textopartner.write({'name': partner.name + " - Actualizado"})- 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 resultBuenas Prácticas
Section titled “Buenas Prácticas”- Actualizar múltiples registros a la vez
# Actualizar múltiples registrosrecords = self.env['model.name'].search([('state', '=', 'draft')])records.write({'state': 'confirmed'})
# Usar operaciones relacionales eficientespartner.write({'category_id': [(4, new_category_id)]})- 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)Método unlink()
Section titled “Método unlink()”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 registrosrecords.unlink()Ejemplos
Section titled “Ejemplos”- Eliminación básica
# Eliminar un registro específicopartner = self.env['res.partner'].browse(1)partner.unlink() # Elimina el partner con ID 1
# Eliminar múltiples registrospartners = self.env['res.partner'].search([('active', '=', False)])partners.unlink() # Elimina todos los partners inactivos- Eliminación con verificación
# Verificar existencia antes de eliminarrecord = self.env['model.name'].browse(999)if record.exists(): record.unlink() _logger.info("Registro eliminado")else: _logger.warning("El registro no existe")- Eliminación condicional
# Eliminar solo registros que cumplan cierta condiciónold_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()- 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()Buenas Prácticas
Section titled “Buenas Prácticas”- Verificar existencia antes de eliminar
# Verificar existencia antes de eliminarif record.exists(): record.unlink()
# Usar unlink() en conjunto de registrosrecords = self.env['model'].search([('active', '=', False)])records.unlink() # ✅ Una sola operación de base de datos- 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)}"}