Odoo sigue una arquitectura similar a MVC, y pasaremos por las capas durante nuestra implementación de la aplicación de tareas pendientes:
Ten en cuenta que el concepto del término controlador mencionado aquí es diferente de los controladores de desarrollo web Odoo. Estos son puntos finales del programa que las páginas web pueden llamar para realizar acciones.
Ahora que Odoo conoce nuestro nuevo módulo, comencemos agregándole un modelo simple.
Los modelos describen objetos de negocio, como una oportunidad, ordenes de clientes o socios (cliente, proveedor, etc.). Un modelo tiene una lista de atributos y también puede definir su negocio específico.
Los modelos se implementan utilizando una clase Python derivada de una clase de plantilla Odoo. Se traducen directamente a objetos de base de datos, y Odoo se encarga de esto automáticamente al instalar o actualizar el módulo. El mecanismo responsable de esto es el Modelo Relacional de Objetos (ORM).
Nuestro módulo será una aplicación muy simple para mantener las tareas pendientes. Estas tareas tendrán un solo campo de texto para la descripción y una casilla de verificación para marcarlas como completas. Más adelante deberíamos añadir un botón para limpiar la lista de tareas de las tareas completas.
Las directrices de desarrollo de Odoo establecen que los archivos Python para los modelos deben colocarse dentro de un subdirectorio models
. Para simplificar, no lo seguiremos aquí, así que vamos a crar un archivo todo_model.py
en el directorio principal del módulo todo_app
.
Añade el siguiente contenido:
# -*- coding: utf-8 -*-
from odoo import models, fields
class TodoTask(models.Model):
_name = 'todo.task'
_description = 'To-do Task'
name = fields.Char('Description', required=True)
is_done = fields.Boolean('Done?')
active = fields.Boolean('Active?', default=True)
La primera línea es un marcador especial que indica al intérprete de Python que este archivo tiene UTF-8 para que pueda esperar y manejar caracteres no ASCII. No usaremos ninguno, pero es una buena práctica tenerlo de todos modos.
La segunda línea es una instrucción de importación de código Python, haciendo disponibles los objetosmodels
y fields
del núcleo Odoo.
La tercera línea declara nuestro nuevo modelo. Es una clase derivada de models.Model
.
La siguiente línea establece el atributo _name
que define el identificador que se utilizará en Odoo para referirse a este modelo. Toma en cuenta que el nombre real de la clase Python, TodoTask
en este caso, carece de significado para otros módulos Odoo. El valor _name
es lo que se utilizará como identificador.
Observa que esta y las siguientes líneas tienen sangría. Si no estás familiarizado con Python, debes saber que esto es importante: la sangría define un bloque de código anidado, por lo que estas cuatro líneas deben tener estar todas igual sangría.
Luego, tenemos el atributo modelo _description
. No es obligatorio, pero proporciona un nombre fácil de usar para los registros del modelo, que puede utilizarse para mejores mensajes de usuario.
Las tres últimas líneas definen los campos del modelo. Vale la pena señalar que name
y active
son nombres de campos especiales. De forma predeterminada, Odoo usará el campo de name
como el título del registro al referenciarlo de otros modelos. El campo active
se utiliza para inactivar los registros y, por defecto, sólo los registros activos serán mostrados. Lo utilizaremos para borrar las tareas completadas sin eliminarlas de la base de datos.
En este momento, este archivo aún no es utilizado por el módulo. Debemos decirle a Python que lo cargue con el módulo en el archivo __init__.py
. Vamos a editarlo para agregar la siguiente línea:
from . Importar todo_modelo
¡Eso es! Para que nuestros cambios de código de Python entren en vigor, la instancia de servidor debe reiniciarse (a menos que esté utilizando el modo --dev
).
No veremos ninguna opción de menú para acceder a este nuevo modelo ya que no los hemos añadido aún. Sin embargo, podemos inspeccionar el modelo recién creado usando el menú Technical. En el menú superior Settings, ve a Technical | Database Structure | Models, busca el modelo todo.task
en la lista y haz clic en él para ver su definición:
Si todo va bien, se confirma que el modelo y los campos fueron creados. Si no puedes verlos aquí, intenta reiniciar el servidor con una actualización de módulo, como se describió anteriormente.
También podemos ver algunos campos adicionales que no declaramos. Estos son campos reservados que Odoo agrega automáticamente a cada modelo nuevo. Estos son los siguientes:
id
es un identificador numérico único para cada registro del modelo.create_date
y create_uid
especifican cuándo se creó el registro y quién lo creó respectivamente.write_date
y write_uid
confirman cuándo el registro fue modificado por última vez y quien lo modificó respectivamente.__last_update
es un ayudante que en realidad no se almacena en la base de datos. Se utiliza para verificaciones de concurrencia.Las mejores prácticas de programación incluyen tener pruebas automatizadas para tu código. Esto es aún más importante para lenguajes dinámicos como Python. Como no hay ningún paso de compilación, no puede estar seguro de que no haya errores sintácticos hasta que el intérprete realmente ejecute el código. Un buen editor puede ayudarnos a detectar estos problemas con antelación, pero no puede ayudarnos a asegurar que el código se ejecute como lo desean las pruebas automatizadas.
Odoo soporta dos formas de describir las pruebas: ya sea utilizando archivos de datos YAML o utilizando código Python, basado en la biblioteca Unittest2
. Las pruebas YAML son un legado de versiones anteriores, y no se recomiendan. Preferiremos usar pruebas de Python y añadiremos un caso básico de prueba a nuestro módulo.
Los archivos de código de prueba deben tener un nombre que empiece por test_
y se debe importar desde tests / __ init__.py
. Pero el directorio de test
(o submódulo Python) no se debe importar desde la parte superior del módulo __init__.py
, ya que se descubrirá y cargará automáticamente sólo cuando se ejecuten pruebas.
Las pruebas deben colocarse en un subdirectorio test/
. Añade un archivo tests / __ init__.py
con lo siguiente:
from . import test_todo
Ahora, añade el código de prueba real disponíble en el archivo tests/test_todo.py
:
# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
class TestTodo(TransactionCase):
def test_create(self):
"Create a simple Todo"
Todo = self.env['todo.task']
task = Todo.create({'name': 'Test Task'})
self.assertEqual(task.is_done, False)
Esto agrega un caso simple de prueba para crear una nueva tarea y verifica que el campo Is Done?Tiene el valor predeterminado correcto.
Ahora queremos hacer nuestras pruebas. Esto se hace agregando la opción --test-enable
durante la instalación del módulo:
$ ./odoo-bin -d todo -i todo_app --test-enable
El servidor Odoo buscará un subdirectorio tests/ en los módulos actualizados y los ejecutará. Si alguna de las pruebas falla, el registro del servidor te mostrará eso.
La capa de vista describe la interfaz de usuario. Las vistas se definen mediante XML, que es utilizado por el marco de cliente web para generar vistas HTML con datos.
Tenemos elementos de menú que pueden activar acciones que pueden hacer vistas. Por ejemplo, la opción de menú Usuarios procesa una acción también denominada Usuarios, que a su vez genera una serie de vistas. Existen varios tipos de vista disponibles, como las vistas de lista y formulario y las opciones de filtro también disponíbles, están definidas por un tipo particular de vista, la vista de búsqueda.
Las directrices de desarrollo de Odoo establecen que los archivos XML que definen la interfaz de usuario deben colocarse dentro de un subdirectorio views /
subdirectorio Comencemos a crear la interfaz de usuario para nuestra aplicación de tareas pendientes.
Ahora que tenemos un modelo para almacenar nuestros datos, debemos hacerlo disponible en la interfaz de usuario.
Para ello, debemos añadir una opción de menú para abrir el modelo To–do Task
para que pueda utilizarse.
Cree el archivo views / todo_menu.xml
para definir un elemento de menú y la acción realizada por él:
<?xml version="1.0"?>
<odoo>
<!-- Action to open To-do Task list -->
<act_window id="action_todo_task"
name="To-do Task"
res_model="todo.task"
view_mode="tree,form" />
<!-- Menu item to open To-do Task list -->
<menuitem id="menu_todo_task"
name="Todos"
action="action_todo_task" />
</odoo>
La interfaz de usuario, incluidas las opciones y las acciones de menú, se almacena en las tablas de la base de datos. El archivo XML es un archivo de datos utilizado para cargar esas definiciones en la base de datos cuando el módulo se instala o actualiza. El código anterior es un archivo de datos Odoo, que describe dos registros para añadir a Odoo:
<act_window>
define una acción de ventana del lado del cliente que abrirá el modelo todo.task
con las vistas de árbol y formulario habilitadas, en ese orden.<menuitem>
define un elemento de menú superior que llama a la acción action_todo_task
, que se definió anteriormente.Ambos elementos incluyen un atributo id. Este atributo id también llamado XML ID, es muy importante: se utiliza para identificar de forma única cada elemento de datos dentro del módulo, y puede ser utilizado por otros elementos para referenciarlo. En este caso, el elemento <menuitem>
necesita hacer referencia a la acción para procesar, y necesita hacer uso de la ID para eso. Los ID XML se tratan con mayor detalle en el Capítulo 4, Datos del módulo
Nuestro módulo aún no conoce el nuevo archivo de datos XML. Esto se hace agregándolo al atributo de datos en el archivo __manifest__.py
. Este, contiene la lista de archivos a cargar por el módulo. Agregue este atributo al diccionario del manifiesto:
'Data': ['views / todo_menu.xml'],
Ahora necesitamos actualizar el módulo de nuevo para que estos cambios surtan efecto. Vaya al menú superior de Todos y debe ver nuestra nueva opción de menú disponible:
Aunque no hemos definido nuestra vista de interfaz de usuario, al hacer clic en el menú Todos se abrirá un formulario generado automáticamente para nuestro modelo, lo que nos permitirá agregar y editar registros.
Odoo es lo suficientemente agradable como para generarlos automáticamente para que podamos empezar a trabajar con nuestro modelo de inmediato.
¡Hasta aquí todo bien! Vamos a mejorar nuestra interfaz de usuario ahora. Trata de hacer mejoras graduales como se muestra en las próximas secciones, haciendo actualizaciones de módulos frecuentes, y no tengas miedo de experimentar. También puedes intentar la opción de servidor --dev = all
. Usándolo, las definiciones de vista se leen directamente desde los archivos XML para que tus cambios puedan estar inmediatamente disponibles para Odoo sin necesidad de una actualización de módulo.
Si una actualización falla debido a un error de XML, no te preocupe! Comenta las últimas porciones XML editadas o elimina el archivo XML de __manifest__.py
y repita la actualización. El servidor debe iniciarse correctamente. Ahora lee el mensaje de error en el registro del servidor con cuidado: debe señalarte dónde está el problema.
Odoo admite varios tipos de vistas, pero las tres más importantes son: tree
(generalmente llamado vistas de lista), form
y search views
. Vamos a añadir un ejemplo de cada uno a nuestro módulo.
Todas las vistas se almacenan en la base de datos, en el modelo ir.ui.view
. Para añadir una vista a un módulo, declaramos un elemento <record>
que describe la vista en un archivo XML, que se va a cargar en la base de datos cuando se instala el módulo.
Agregue este nuevo archivo views / todo_view.xml
para definir nuestra vista de formulario:
<?xml version="1.0"?>
<odoo>
<record id="view_form_todo_task" model="ir.ui.view">
<field name="name">To-do Task Form</field>
<field name="model">todo.task</field>
<field name="arch" type="xml">
<form string="To-do Task">
<group>
<field name="name"/>
<field name="is_done"/>
<field name="active" readonly="1"/>
</group>
</form>
</field>
</record>
</odoo>
Recuerde agregar este nuevo archivo a la clave de datos en el archivo de manifiesto, de lo contrario, nuestro módulo no lo sabrá y no se cargará.
Esto agregará un registro al modelo ir.ui.view
con el identificador view_form_todo_task
. La vista es para el modelo todo.task
y se denomina To-do Task Form
. El nombre es solo para información; No tiene que ser único, pero debe permitir que uno identifique fácilmente a qué registro se refiere. De hecho, el nombre puede ser totalmente omitido, en ese caso, se generará automáticamente a partir del nombre del modelo y el tipo de vista.
El atributo más importante es arch
, y contiene la definición de la vista, resaltada en el código XML anterior. La etiqueta <form>
define el tipo de vista y, en este caso, contiene tres campos. También agregamos un atributo al campo active
para que sea solo de lectura.
La sección anterior proporcionó una vista de formulario básica, pero podemos hacer algunas mejoras en ella. Para los modelos de documentos, Odoo tiene un estilo de presentación que imita una página en papel. Este formulario contiene dos elementos: <header>
para contener los botones de acción y <sheet>
para contener los campos de datos.
Ahora podemos reemplazar el <form>
básico definido en la sección anterior por éste:
<header>
<!-- Buttons go here-->
</header>
<sheet>
<!-- Content goes here: -->
<group>
<field name="name"/>
<field name="is_done"/>
<field name="active" readonly="1"/>
</group>
</sheet>
</form>
Los formularios pueden tener botones para realizar acciones. Estos botones pueden ejecutar acciones de ventana como abrir otro formulario o ejecutar funciones de Python definidas en el modelo.
Pueden colocarse en cualquier lugar dentro de un formulario, pero para los formularios de estilo de documento, el lugar recomendado para ellos es la sección <header>
.
Para nuestra aplicación, agregaremos dos botones para ejecutar los métodos del modelo todo.task
:
<header>
<button name="do_toggle_done" type="object"
string="Toggle Done" class="oe_highlight" />
<button name="do_clear_done" type="object"
string="Clear All Done" />
</header>
Los atributos básicos de un botón comprenden lo siguiente:
stryng
con el texto a mostrar en el botóntype
de acción que realizaname
es el identificador de esa acciónclass
es un atributo opcional para aplicar estilos CSS, como en HTML normalLa etiqueta <group>
te permite organizar el contenido del formulario. Colocar elementos <group>
dentro de un elemento <group>
crea un diseño de dos columnas dentro del grupo externo. Se aconseja que los elementos del grupo tengan un atributo de nombre para que sea más fácil para otros módulos extenderlos.
Usaremos esto para organizar mejor nuestro contenido. Cambiemos el contenido <sheet>
de nuestro formulario para que coincida con este:
<sheet>
<group name="group_top">
<group name="group_left">
<field name="name"/>
</group>
<group name="group_right">
<field name="is_done"/>
<field name="active" readonly="1"/>
</group>
</group>
</sheet>
En este punto, nuestro formulario todo.task
debe verse así:
<form>
<header>
<button name="do_toggle_done" type="object"
string="Toggle Done" class="oe_highlight" />
<button name="do_clear_done" type="object"
string="Clear All Done" />
</header>
<sheet>
<group name="group_top">
<group name="group_left">
<field name="name"/>
</group>
<group name="group_right">
<field name="is_done"/>
<field name="active" readonly="1" />
</group>
</group>
</sheet>
</form>
Recuerda que para que los cambios se carguen en nuestra base de datos Odoo, se necesita una actualización del módulo. Para ver los cambios en el cliente web, el formulario debe ser recargado: haz clic de nuevo en la opción de menú que lo abre o vuelve a cargar la página del navegador (F5 en la mayoría de los navegadores).
Los botones de acción no funcionarán aún, ya que todavía necesitamos agregar su lógica de negocio.
Cuando se visualiza un modelo en modo de lista, se utiliza una vista <tree>
. Las vistas de árbol son capaces de mostrar líneas organizadas en jerarquías, pero la mayoría de las veces, se utilizan para mostrar listas sin formato.
Podemos agregar la siguiente definición de vista tree
a todo_view.xml
:
<record id="view_tree_todo_task" model="ir.ui.view">
<field name="name">To-do Task Tree</field>
<field name="model">todo.task</field>
<field name="arch" type="xml">
<tree colors="decoration-muted:is_done==True">
<field name="name"/>
<field name="is_done"/>
</tree>
</field>
</record>
Esto define una lista con sólo dos columnas: name
y is_done
. También añadimos un toque agradable: las líneas para las tareas hechas (is_done == True
) se muestran en gris. Esto se hace aplicando la clase silenciada Bootstrap. Consulta http://getbootstrap.com/css/#helper-classes-colors para obtener más información sobre Bootstrap y sus colores contextuales.
En la esquina superior derecha de la lista, Odoo muestra un cuadro de búsqueda. Los campos que busca y los filtros disponibles se definen mediante una vista <search>
.
Como antes, agregamos esto a todo_view.xml
:
<record id="view_filter_todo_task" model="ir.ui.view">
<field name="name">To-do Task Filter</field>
<field name="model">todo.task</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<filter string="Not Done"
domain="[('is_done','=',False)]"/>
<filter string="Done"
domain="[('is_done','!=',False)]"/>
</search>
</field>
</record>
Los elementos <field>
definen campos que también se buscan al escribir en el cuadro de búsqueda. Los elementos <filter>
añaden condiciones de filtro predefinidas, que se pueden alternar con un clic de usuario, definido mediante el uso de una sintaxis específica.
Ahora vamos a añadir algo de lógica a nuestros botones. Esto se hace con código Python, utilizando los métodos de la clase de modelos Python.
Debemos editar el archivo Python todo_model.py
para agregar a la clase los métodos llamados por los botones. Primero, necesitamos importar la nueva API, así que agréguala a la declaración de importación en la parte superior del archivo Python:
from odoo import models, fields, api
La acción del botón Toggle Done será muy simple: solo cambia la bandera Is Done?. Para la lógica de los registros, utiliza el decorador @api.multi
. Aquí, self
representará un conjunto de registros, y entonces deberíamos hacer un bucle a través de cada registro.
Dentro de la clase TodoTask, añade esto:
@api.multi
def do_toggle_done(self):
for task in self:
task.is_done = not task.is_done
return True
El código pasa por todos los registros de tarea y, para cada uno, modifica el campo is_done
, invirtiendo su valor. El método no necesita devolver nada, pero debemos tenerlo al menos para devolver un valor True
. La razón es que los clientes pueden utilizar XML-RPC para llamar a estos métodos y este protocolo no admite funciones de servidor devolviendo sólo un valor None
.
Para el botón Clear All Done, queremos ir un poco más lejos. Debe buscar todos los registros activos que están hechos, y hacerlos inactivos. Normalmente, se espera que los botones de formulario actúen sólo en el registro seleccionado, pero en este caso, queremos que actúe también en registros distintos del actual:
@api.model
def do_clear_done(self):
dones = self.search([('is_done', '=', True)])
dones.write({'active': False})
return True
En los métodos decorados con @ api.model
, la variable self
representa el modelo sin registro en particular. Construiremos un conjunto de registros dones
que contenga todas las tareas marcadas como terminadas. A continuación, establecemos el indicador active
para False
en ellos.
El método de búsqueda es un método API que devuelve los registros que cumplen algunas condiciones. Estas condiciones están escritas en un dominio, que es una lista de tripletes. Exploraremos los dominios con más detalle en el Capítulo 6, Vistas – Diseñando la interfaz de usuario.
El método write
establece los valores de una vez en todos los elementos del conjunto de registros. Los valores a escribir se describen utilizando un diccionario. Usar write here
es más eficiente que iterar a través del conjunto de registros para asignar el valor a cada uno de ellos uno por uno.
Ahora debemos agregar pruebas para la lógica de negocio. Idealmente, queremos que cada línea de código sea cubierta por al menos un caso de prueba. En tests / test_todo.py
, agregua unas cuantas líneas más de código al método test_create ()
:
# def test_create(self):
# ...
# Test Toggle Done
task.do_toggle_done()
self.assertTrue(task.is_done)
# Test Clear Done
Todo.do_clear_done()
self.assertFalse(task.active)
Si ahora ejecutamos las pruebas y los métodos del modelo están correctamente escritos, no deberíamos ver ningún mensaje de error en el registro del servidor:
$ ./odoo-bin -d todo -i todo_app --test-enable
Es posible que haya notado que, al cargar, nuestro módulo recibe un mensaje de advertencia en el registro del servidor:
The model todo.task has no access rules, consider adding one.
(El modelo todo.task no tiene reglas de acceso, considere agregar una.)
El mensaje es bastante claro: nuestro nuevo modelo no tiene reglas de acceso, por lo que no puede ser utilizado por nadie que no sea el superusuario de admin. Como superusuario, el admin ignora las reglas de acceso a datos, y es por eso que hemos podido utilizar el formulario sin errores. Pero debemos corregir esto antes de que otros usuarios puedan usar nuestro modelo.
Otra cuestión que todavía tenemos que abordar es que queremos que las tareas pendientes sean privadas para cada usuario. Odoo soporta reglas de acceso a nivel de fila, que usaremos para implementar eso.
De hecho, nuestras pruebas deben estar fallando en este momento debido a las reglas de acceso que faltan. Ellas no están porque se hacen con el usuario admin. Por lo tanto, debemos cambiarlos para que utilicen el usuario Demo en su lugar.
Para ello, debemos editar el archivo tests / test_todo.py
para añadir un método setUp
:
# class TestTodo(TransactionCase):
def setUp(self, *args, **kwargs):
result = super(TestTodo, self).setUp(*args, \
**kwargs)
user_demo = self.env.ref('base.user_demo')
self.env= self.env(user=user_demo)
return result
Esta primera instrucción llama al código setUp
de la clase padre. Los siguientes cambian el entorno utilizado para ejecutar las pruebas, self.env
, a una nueva usando el usuario Demo
. No se necesitan más cambios en las pruebas que ya escribimos.
También debemos añadir un caso de prueba para asegurarnos de que los usuarios sólo pueden ver sus propias tareas. Para ello, primero, agregua una importación adicional en la parte superior:
from odoo.exceptions import AccessError
A continuación, agregua un método adicional a la clase de prueba:
def test_record_rule(self):
"Test per user record rules"
Todo = self.env['todo.task']
task = Todo.sudo().create({'name': 'Admin Task'})
with self.assertRaises(AccessError):
Todo.browse([task.id]).name
Dado que nuestro método env
ahora está utilizando el usuario de Demo, usamos el método sudo ()
para cambiar el contexto al usuario admin. A continuación, lo usamos para crear una tarea que no debería ser accesible para el usuario Demo.
Al intentar acceder a los datos de esta tarea, esperamos que se genere una excepción AccessError
.
Si ejecutamos las pruebas ahora, deberían fallar, así que nos encargamos de eso.
Para obtener una imagen de qué información se necesita para agregar reglas de acceso a un modelo, utiliza el cliente web y ve a Settings | Technical | Security | Access Controls List :
Aquí podemos ver la ACL de algunos modelos. Indica, por grupo de seguridad, qué acciones se permiten en los registros.
Esta información debe ser proporcionada por el módulo utilizando un archivo de datos para cargar las líneas en el modelo ir.model.access
. Vamos a agregar acceso completo al grupo de empleados en el modelo. El empleado es el grupo básico de acceso al que casi todos pertenecen.
Esto se hace utilizando un archivo CSV denominado security / ir.model.access.csv
. Vamos a agregarlo con el siguiente contenido:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
acess_todo_task_group_user,todo.task.user,model_todo_task,base.group_user,1,1,1,1
El nombre de archivo corresponde al modelo para cargar los datos, y la primera línea del archivo tiene los nombres de columna. Estas son las columnas proporcionadas en nuestro archivo CSV:
id
es el identificador externo del registro (también conocido como XML ID). Debe ser único en nuestro módulo.name
es un título de descripción. Es sólo informativo y es mejor si se mantiene único. Los módulos oficiales normalmente usan una cadena separada por puntos con el nombre del modelo y el grupo. Siguiendo esta convención, utilizamos todo.task.user
.model_id
es el identificador externo del modelo al que estamos dando acceso. Los modelos tienen XML IDs generados automáticamente por el ORM: para todo.task
, el identificador es model_todo_task
.group_id
identifica el grupo de seguridad para dar permisos. Los más importantes son proporcionados por el módulo base. El grupo Empleado es un caso así y tiene el identificador base.group_user
.perm
marcan el acceso a garantizar read
, write
, create
o un link
(borrar) el acceso.No debemos olvidar añadir la referencia a este nuevo archivo en el atributo de datos del descriptor __manifest__.py
. Debe tener un aspecto como este:
'data': [
'security/ir.model.access.csv',
'views/todo_view.xml',
'views/todo_menu.xml',
],
Como antes, actualice el módulo para que estas adiciones entren en vigor. El mensaje de advertencia debe desaparecer, y podemos confirmar que los permisos están bien iniciando sesión con el usuario demo
(la contraseña también es demo
). Si ejecutamos nuestras pruebas ahora solo deberían fallar el caso de prueba test_record_rule
.
Podemos encontrar la opción Record Rules en el menú Technical, junto con *Access Control List.
Las reglas de registro se definen en el modelo ir.rule
. Como de costumbre, necesitamos proporcionar un nombre distintivo. También necesitamos el modelo en el que operan y el filtro de dominio que se utilizará para la restricción de acceso. El filtro de dominio utiliza la lista usual de tuplas sintáctica utilizada en Odoo.
Por lo general, las reglas se aplican a algunos grupos de seguridad en particular. En nuestro caso, lo haremos aplicable al grupo Empleados. Si no se aplica a ningún grupo de seguridad en particular, se considera global (el campo global
se establece automáticamente en True
). Las reglas globales son diferentes porque imponen restricciones que las reglas no globales no pueden anular.
Para agregar la regla de registro, debemos crear un archivo security / todo_access_rules.xml
con el siguiente contenido:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="todo_task_user_rule" model="ir.rule">
<field name="name">ToDo Tasks only for owner</field>
<field name="model_id" ref="model_todo_task"/>
<field name="domain_force">
[('create_uid','=',user.id)]
</field>
<field name="groups" eval="
[(4,ref('base.group_user'))]"/>
</record>
</data>
</odoo>
Observa el atributo noupdate = "1"
. Significa que estos datos no se actualizarán en actualizaciones de módulos. Esto le permitirá ser personalizado más adelante ya que las actualizaciones de módulos no destruirán los cambios realizados por el usuario. Pero ten en cuenta que esto también será el caso durante el desarrollo, por lo que es posible que desees establecer noupdate = "0"
durante el desarrollo hasta que estéss satisfecho con el archivo de datos.
En el campo de grupos, también encontrarás una expresión especial. Es un campo relacional de uno a muchos, y tienen una sintaxis especial para operar. En este caso, la tupla (4, x) indica anexar x
a los registros, y aquí x
es una referencia al grupo Empleados, identificado por base.group_user
. Esta sintaxis especial de escritura de uno-a-muchos se discute con más detalle en el Capítulo 4, Datos de Módulo.
Como antes, debemos añadir el archivo a __manifest__.py
antes de poder cargarlo en el módulo:
'data': [
'security/ir.model.access.csv',
'security/todo_access_rules.xml',
'todo_view.xml',
'todo_menu.xml',
],
Si lo hicimos bien, podemos ejecutar las pruebas de módulo y ahora deben pasar.
Nuestro módulo se ve bien. ¿Por qué no añadir un icono para que se vea aún mejor? Para esto, solo necesitamos agregar al módulo un archivo static / description / icon.png
con el icono que se va a usar.
Estaremos reutilizando el icono de la aplicación existente Notes, por lo que deberíamos copiar el archivo odoo / addons / static / description / icon.png
en el directorio addons / todo_app / static / description
.
Los siguientes comandos deben hacer ese truco para nosotros:
$ mkdir -p ~/odoo-dev/custom-addons/todo_app/static/description
$ cp ~/odoo-dev/odoo/addons/note/static/description/icon.png ~/odoo-dev/custom-addons/todo_app/static/description
Ahora, si actualizamos la lista de módulos, nuestro módulo debe mostrarse con el nuevo icono.
También podemos añadir una descripción mejor para explicar lo que hace y lo grandioso que es. Esto se puede hacer en la clave description
del archivo __manifest__.py
. Sin embargo, la forma preferida es agregar un archivo README.rst
al directorio raíz del módulo.
Hemos creado un nuevo módulo desde el principio, cubriendo los elementos más utilizados en un módulo: modelos, los tres tipos básicos de vistas (formulario, lista y búsqueda), lógica empresarial en los métodos de modelo y seguridad de acceso.
En el proceso, nos familiarizamos con el proceso de desarrollo de módulos, que implica actualizaciones de módulos y reinicios del servidor de aplicaciones para hacer que los cambios graduales sean efectivos en Odoo.
+
Recuerde siempre, cuando se agregan campos del modelo, se necesita una actualización. Al cambiar el código de Python, incluyendo el archivo de manifiesto, se necesita un reinicio. Al cambiar archivos XML o CSV, se necesita una actualización; También, en caso de duda, haz lo siguiente: reinicia el servidor y actualiza los módulos.
En el siguiente capítulo, aprenderás cómo construir módulos que se apilarán en los existentes para agregar características.