Publicidad

jueves, 7 de mayo de 2015

API ODOO: Metaclases y Decodarores

Implementación de la API de Odoo Utilizando Metaclases y Decoradores



API:

    API por sus siglas en Ingles Application Programming Interface ( Interfaz de Programación de Aplicaciones), es el conjunto de subrutinas, funciones y procedimientos (o métodos, en la programación orientada a objetos) que ofrece cierta biblioteca para ser utilizado por otro software como una capa de abstracción. Son usadas generalmente en las bibliotecas. Fuente: wikipedia.org

Metaclases:

    A metaclase puede definirse como   "Una clase de Clases". En programación orientada a objetos, una metaclase es una clase cuyas instancias son clases. En otras palabras, como los objetos son instancias de una clase, las clases son instancias de una metaclase.

Detalles para Implementar la API en sus Desarrollos:

 Odoo provee 2 formas de Implementar las Clases (Modelos), para la generación de Formularios, podríamos llamarlas como la forma "Tradicional" y a través de "Recordsets". Un recordset es la instancia de una clase, con la referencia del registro en Base de Datos (ID).

    En la forma tradicional utilizamos siempre los parámetros cursor de la base de datos (cr), id usuario (uid), un listado de los Registros (ids) y context, cada vez que definimos un Método de la Clase. Utilizando el método por Recordsets mediante Decoradores, estos parámetros están Implícitos dentro de la API, aquí debemos comprender que si se trabaja con la API de Odoo, todo lo que sea retornado mediante los métodos del ORM siempre serán records.

Ejemplo con el Metodo Tradicional:

    modelo = self.pool.get(Modelo.) # Instanciamos una Clase (Modelo)
    ids = modelo.search(cr, uid, [Dominio con Condiciones para la Busqueda], context=context) 
    # Una funcion search con la forma Tradicional siempre Rertorna una lista de IDS
    for rec in modelo.browse(cr, uid, ids, context=context):
        print rec.name
    modelo.write(cr, uid, ids, {Diccionario de Valores a Actualizar}, context=context)

Ejemplo Utilizando la API de Odoo:

    env = env(cr, uid, context)         # cr, uid, context, implicitos en la instancia env
    recs = env[Modelo]                    # Instanciamos una Clase(Modelo) de Odoo
    recs = recs.search([Dominio con Condiciones para la Busqueda de Registros])          
   #  Una busqueda utilizando env y la API, siempre retornada una lista de Recordsets.
    for rec in recs:                             # Podemos recorrer cada record para poder trabajar con ellos.
        print rec.name
    recs.write({Diccionario de Valores a Actualizar})

Como pudimos observar, los parámetros tradicionales (cr, uid, ids, context) están Implícitos al trabajar con la API de Odoo, algo que puede confundirnos es al momento de trabajar con Ambas.

Decoradores:

Los decoradores en Odoo, son utilizados para tratar de enviar los argumentos que el Sistema sabe que tiene que recibir de forma Obligatoria como el Ejemplo Anterior (cr, uid, ids, context).

Los Decoradores con la API Odoo, nos ayudan a Decorar las funciones tradicionales y a Implementar una nueva Funcionalidad.

Ejemplos:
    
1. @api.cr:

Para decorar funciones que siempre Reciben Como Parametro CR, utilizariamos este Decorador.

    # recs = modelo.browse(cr, uid, ids, context)
    recs.method(args)
    model.method(cr, args)

2. @api.cr_context:

Decoramos Funciones que utilizan siempre 'cr', 'context' como Parametros.
  
3. @api.cr_uid:

Decoramos Funciones que utilizan siempre 'cr', 'uid' como Parametros.

4. @api.cr_uid_context:

Decoramos Funciones que utilizan siempre 'cr', 'uid', 'context' como Parametros. 

    # recs = modelo.browse(cr, uid, ids, context)
    recs.method(args)

    modelo.metodo(cr, uid, args, context=context)

5. @api.cr_uid_id:

Decoramos Funciones que utilizan siempre 'cr', 'uid', 'ids' como Parametros. Podriamos utilizar un Decorador de este tipo para retornar siempre un Recordset sin tener que generar la instancia y pasar los Parametros.


6. @api.cr_uid_id:

Este Ejemplo seria el mas claro si estamos acostumbrados a definir funciones con OpenERP, en donde siempre eran requeridos los parametros 'cr', 'uid', 'id':

    @api.cr_uid_id
    def metodo(self, cr, uid, id, args, context=None):
        ...

7. @api.cr_uid_ids:

Este Decorador seria el mismo que el anterior solo que aquí se enviaría el parámetro IDS con un listado de Registros, en el anterior solo podríamos enviar un solo ID.

8. @api.cr_uid_ids_context:

Este Decorador como su nombre lo describe, recibe todos los parametros para poder generar Funciones. Recibe 'cr', 'uid', 'ids', 'context', parametros que siempre necesitamos para trabajar con Odoo.

Decoradores que utiliza la API de Odoo

1. @api.model:

Este Decorador solo genera una Instancia de una Clase en Odoo (Modelo). Su sintaxis es la siguiente:

    @api.model
    def metodo(self, args):
        ...



2. @api.one:

Este decorador sirve para poder obtener un recordset de la Clase, siempre que sea utilizado en un Metodo. Es equivalente a realizar un browse de una Clase de Odoo (self.browse(cr, uid, id, context=None) ), con este decorador podriamos obtener un valor de esa Clase simplemente con retornar self.campo_a_obtener. Su sintaxis es la siguiente:

    @api.one
    def metodo(self, args):
        return self.name

Con el Método Tradicional Seria:

    def metodo(self, cr, uid, id, context=None):
        inst = self.browse(cr, uid, id, context=None)
        return inst.name

3. @api.multi:

Este Decorador es similar al anterior, solo que aquí retorna un listado de recordsets. Su sintaxis es la siguiente:

    @api.multi
    def method(self, args):
        ...
       

En la forma tradicional seria definir un ciclo con el metodo Browse:
    
    def metodo(self, cr, uid, ids, context=None):
        for rec in self.browse(cr, uid, ids, context=None)
             print "#### Campo Name de cada Registro >>> ", rec.name
        return True

En la forma tradicional solíamos obtener los ids a recorrer usando el método Search.

4. @api.constrains:

Como podemos observar por su nombre, este decorador nos permite decorar funciones para crear Restricciones a la Inserción de Información, los llamados Constraints. Su sintaxis es la siguiente:

    @api.one
    @api.constrains('name', 'description')
    def _check_description(self):
        if self.name == self.description:
            raise ValidationError("Los Campos Nombre y Descripción no pueden ser los Mismos")

Para que quede claro, un ejemplo con la Forma tradicional:

    def _check_description(self, cr, uid, ids, context=None):
        for rec in self.browse(cr, uid, ids, context=None):
            if rec.name == rec.description:
                 return False
         return True

_constraints = [(_check_description, 'Error: Los Campos Nombre y Descripcion No pueden ser Iguales', ['name','description']), ] 

5. @api.onchange:

    Si has llegado a este Decorador ya tienes la idea de lo que ah tratado de hacer Odoo, que es tratar de Optimizar el Código enfocándonos directamente en la lógica del requerimiento, sin tener que ser recurrentes con los parámetros, este Decorador nos sirve para poder crear métodos on_change (Métodos que se ejecutar al cambiar un Valor del Formulario en donde este asignado este Metodo).
Su sintaxis es la siguiente:

    @api.onchange('partner_id')
    def _onchange_partner(self):
        recs = env['res.partner']                    
        recs = recs.search([('parent_id','=',self.partner_id)]) 

        self.partner_id = recs[0].id

Observamos que ahora ya no es necesario retornar Valores en un diccionario del tipo {'value':{Campos a Actualizar}. Observamos que el Decorador recibe parámetros, que en este caso son los campos que necesitamos para poder trabajar como es  'partner_id'.

Ejemplo con la forma Tradicional:
    def _onchange_partner(self, cr, uid, ids, partner_id, context=None):
          partner_id = self.pool.get('res.partner').search(cr, uid, [('parent_id','=',partner_id)], context)
          if partner_id:
               return {'value':{'partner_id': partner_id[0]}}
          return {}

     

6. @api.depends:

Este Decorador es utilizado para campos Calculados, o campos que requieran obtener un listado de valores:

    pname = fields.Char(compute='_compute_pname')

    @api.one
    @api.depends('partner_id.name', 'partner_id.is_company')
    def _compute_pname(self):
        if self.partner_id.is_company:
            self.pname = (self.partner_id.name or "").upper()
        else:
            self.pname = self.partner_id.name

7.@api.returns:

Decorador que permite retornar instancias de un Modelo. Sus sintaxis es:

    @model
    @returns('res.partner') # Modelo a Recibir
    def find_partner(self, arg):
        ...     # retorna algun recordset


Decoradores para Interactuar con los Nuevos estilos utilizando una version Tradicional.

1. @api.v7:

Este Decorador permite utilizar metodos de la Nueva API, generando Clases (Modelos) de la forma Tradicional. Su sintaxis es:

    @api.v7
    def foo(self, cr, uid, ids, context=None):
        ...

    @api.v8
    def foo(self):
        ...


Decoradores para Interactuar con el estilo Tradicional utilizando una version con la nueva API.

1. @api.v8:

    @api.v8
    def foo(self):
        ...

    @api.v7
    def foo(self, cr, uid, ids, context=None):
        ...

Espero que les pueda ayudar a comprender la nueva API, aun me faltan muchas cosas por Comprender que estaré subiendo.


5 comentarios:

  1. Hola te ceutno que estoy pasando un modulo de Openerp 7 a ODOO8 y no hay manera de que el ir_cron o planificador de tareas funcione... coloque el metodo tal cual como esta en Openerp asi

    @api.v7
    def create(self, cr, uid, context=None):

    Pero no funciona cuando pasa el minuto, dice :
    File "/opt/odoo/odoo-server/openerp/addons/base/ir/ir_cron.py", line 138, in _callback
    getattr(model, method_name)(cr, uid, *args)
    File "/opt/odoo/odoo-server/openerp/api.py", line 268, in wrapper
    return old_api(self, *args, **kwargs)
    File "/opt/odoo/odoo-server/openerp/api.py", line 372, in old_api
    result = method(recs, *args, **kwargs)
    TypeError: create() takes exactly 2 arguments (1 given)

    Tal vez puedas ayudarme, le coloque print "Algo" en la primera linea de la funcion y nada :S, tal vez tengas alguna idea?

    ResponderEliminar
  2. Disculpa ya el problema fue solucionado, en argumentos se le debe colocar si o si ()

    Gracias y buen dia

    ResponderEliminar