Odoo shell: differenze tra le versioni

Da PNLUG.
(Esempi di script)
m (Aggiornato link per repository github)
 
(17 versioni intermedie di 2 utenti non mostrate)
Riga 3: Riga 3:
 
Può essere comodo, per diversi motivi, eseguire dei comandi python (in maniera interattiva o con degli script) in una shell, interagendo con tutto l'ambiente messo a disposizione da odoo.
 
Può essere comodo, per diversi motivi, eseguire dei comandi python (in maniera interattiva o con degli script) in una shell, interagendo con tutto l'ambiente messo a disposizione da odoo.
  
Per avere a disposizione una shell in cui eseguire dei comandi, è sufficiente aggiungere il sottocomando ''odoo'' al comando di avvio. Ad esempio (con riferimento allo script di avvio preparato per gli incontri del LUG dedicati allo sviluppo):
+
Per avere a disposizione una shell in cui eseguire dei comandi, è sufficiente aggiungere il sottocomando ''shell'' al comando di avvio. Ad esempio (con riferimento allo script di avvio preparato per gli incontri del LUG dedicati allo sviluppo):
  
 
<pre>
 
<pre>
Riga 14: Riga 14:
 
$ODOOHOME/OCB/odoo-bin shell -d tutorial1  --addons-path=$ODOOHOME/OCB/addons,$ODOOHOME/addons_ita,$ODOOHOME/addons,$ODOOHOME/custom_addons
 
$ODOOHOME/OCB/odoo-bin shell -d tutorial1  --addons-path=$ODOOHOME/OCB/addons,$ODOOHOME/addons_ita,$ODOOHOME/addons,$ODOOHOME/custom_addons
 
</pre>
 
</pre>
 +
 +
== Persistenza delle modifiche ==
 +
 +
'''Attenzione''': qualora si apportino delle modifiche ai dati in una shell (in modalità interattiva o con l'esecuzione di uno script), esse '''non''' persisteranno nel database, perché viene effettuato un ''rollback()'' al termine dell'esecuzione. (Prima del 14 aprile il comportamento non era questo, visto che se la sessione veniva chiusa premendo ctrl-D non veniva effettuato il ''rollback()''. Questo è stato considerato un [https://github.com/odoo/odoo/commit/ea2faefe0d6a4a6063d48fc2cda8d7071d9eefa4| bug] ed è stato corretto.)
 +
 +
È possibile rendere persistenti le modifiche effettuate facendo esplicitamente un ''commit()'' con l'istruzione (che deve essere l'ultima ad essere eseguita nello script):
 +
 +
<pre>
 +
self.env.cr.commit()
 +
</pre>
 +
 +
Ciò contraddice il principio [https://doc.odoo.com/contribute/15_guidelines/coding_guidelines_framework/#never-commit-the-transaction| Never commit the transaction], che però probabilmente vale per il codice scritto per essere eseguito via web, non per gli script da eseguire nella shell. D'altronde, anche il [https://www.packtpub.com/big-data-and-business-intelligence/odoo-10-development-essentials| testo di Reis] suggerisce di dare questo comando per rendere persistenti le modifiche.
 +
 +
== Modalità di lavoro interattiva ==
  
 
Con l'esecuzione di questo script non verrà avviato, come di consueto, il server web interno di odoo, ma si aprirà invece una shell nella quale, in maniera interattiva, sarà possibile eseguire dei comandi.
 
Con l'esecuzione di questo script non verrà avviato, come di consueto, il server web interno di odoo, ma si aprirà invece una shell nella quale, in maniera interattiva, sarà possibile eseguire dei comandi.
Riga 32: Riga 46:
 
Nella shell, ''self'' è un'istanza del Recordset Model ''res.users'' e -- in particolare -- quello con id=1, ossia Administrator.
 
Nella shell, ''self'' è un'istanza del Recordset Model ''res.users'' e -- in particolare -- quello con id=1, ossia Administrator.
  
=== Esecuzione di script ===
+
 
 +
== Esecuzione di script ==
  
 
Per cose più complesse, naturalmente, è possibile preparare degli script da far eseguire alla shell, che quest'ultima leggerà dallo standard input. Ad esempio, supponendo di avere uno script ''test.py'' nella directory ''odooscripts'', si potrà digitare il comando  
 
Per cose più complesse, naturalmente, è possibile preparare degli script da far eseguire alla shell, che quest'ultima leggerà dallo standard input. Ad esempio, supponendo di avere uno script ''test.py'' nella directory ''odooscripts'', si potrà digitare il comando  
  
 
<pre>
 
<pre>
./odoo_cli.sh < test.py
+
./odoo_cli.sh < odooscripts/test.py
 
</pre>
 
</pre>
  
=== Esempi di script ===
+
== Esempi di script ==
  
Qui di seguito descriviamo qualche script per dare l'idea di che cosa si possa fare e come farlo. I sorgenti sono disponibili anche su [https://github.com/loristissino/odooscripts GitHub].
+
Qui di seguito descriviamo qualche script per dare l'idea di che cosa si possa fare e come farlo. I sorgenti sono disponibili anche su [https://github.com/loristissino/odooshellscripts GitHub].
  
==== Visualizzazione dei modelli registrati ====
+
=== Visualizzazione dei modelli registrati ===
  
 
<pre>
 
<pre>
Riga 58: Riga 73:
 
</pre>
 
</pre>
  
==== Elenco degli utenti ====
+
Nota: è anche possibile ordinare alfabeticamente i modelli con:
 +
<pre>
 +
for model in sorted(self.env):
 +
    print model
 +
</pre>
 +
o generare una lista ordinabile con:
 +
<pre>
 +
models = [i for i in self.env]
 +
models.sort()
 +
for model in models:
 +
    print model
 +
</pre>
 +
 
 +
=== Elenco degli utenti ===
  
 
<pre>
 
<pre>
Riga 78: Riga 106:
 
</pre>
 
</pre>
  
==== Elenco degli allegati ====
+
=== Elenco delle voci di configurazione ===
  
Di default, gli allegati vengono registrati nel filesystem in forma di file binario in una sottodirectory della directory indicata nel file di configurazione (?) -- ad esempio, in ''~/.local/share/Odoo/filestore''. Il nome del file è dato dall'sha1sum dello stesso, e i file vengono suddivisi in 256 directory che hanno come nome i primi due caratteri dello stesso sha1sum.
+
<pre>
 +
# -*- coding: utf-8 -*-
 +
 
 +
'''
 +
This script is meant to be executed inside the odoo shell,
 +
not on its own
 +
'''
 +
 
 +
for key in odoo.tools.config.options:
 +
    print key, '=', odoo.tools.config.options[key]
 +
 
 +
 
 +
# It is possible to retrieve a specific configuration option, of course:
 +
print
 +
print "Log level:"
 +
print odoo.tools.config.options['log_level']
 +
</pre>
 +
 
 +
=== Elenco degli allegati ===
 +
 
 +
Di default, gli allegati vengono registrati nel filesystem in forma di file binario in una sottodirectory della directory indicata nei parametri di configurazione (vedi script precedente) -- ad esempio, in ''~/.local/share/Odoo/filestore''. Il nome del file è dato dall'sha1sum dello stesso, e i file vengono suddivisi in 256 directory che hanno come nome i primi due caratteri dello stesso sha1sum.
  
 
I riferimenti agli allegati sono mantenuti nel database nella tabella ''ir.attachment''. Con questo script li si può trovare come file binari ed associare al loro mimetype e al loro nome originale:
 
I riferimenti agli allegati sono mantenuti nel database nella tabella ''ir.attachment''. Con questo script li si può trovare come file binari ed associare al loro mimetype e al loro nome originale:
Riga 95: Riga 143:
 
# retrieves a Recordset Model for the Attachment class
 
# retrieves a Recordset Model for the Attachment class
  
for attachment in Attachment.search([]):
+
for attachment in Attachment.search([('res_model', '!=', False)]):
     print attachment.store_fname, attachment.mimetype, attachment.datas_fname, attachment.file_size
+
     print attachment.store_fname, attachment.mimetype, attachment.datas_fname, attachment.file_size, attachment.res_model
 +
 
  
 +
print "Files are stored in %s/filestore/%s" % (
 +
    odoo.tools.config.options['data_dir'], odoo.tools.config.options['db_name']
 +
    )
 
</pre>
 
</pre>
  
==== Ricerca e visualizzazione ====
+
=== Ricerca e visualizzazione ===
  
''Da qui in avanti faremo riferimento ad una classe, "TodoTask", descritta nel libro "Odoo 10 Development Essentials".''
+
''Da qui in avanti faremo riferimento ad una classe, "TodoTask", descritta nel libro [https://www.packtpub.com/big-data-and-business-intelligence/odoo-10-development-essentials| Odoo 10 Development Essentials] di Daniel Reis.
  
 
Questo script recupera tutti i task con determinate caratteristiche e ne mostra le informazioni rilevanti sullo standard output:
 
Questo script recupera tutti i task con determinate caratteristiche e ne mostra le informazioni rilevanti sullo standard output:
Riga 143: Riga 195:
 
</pre>
 
</pre>
  
==== Creazione di task ====
+
=== Creazione di task ===
  
 
Questo script crea una decina di task con nomi caratterizzati da un numero progressivo, contrassegnandoli in maniera alternata come svolti o da svolgere:
 
Questo script crea una decina di task con nomi caratterizzati da un numero progressivo, contrassegnandoli in maniera alternata come svolti o da svolgere:
Riga 159: Riga 211:
  
 
for i in range(10):
 
for i in range(10):
     newtask = TodoTask.create( { 'name': 'Nuovo task %03d' % i } )
+
     is_done = i % 2 == 0
    newtask.is_done = i % 2 == 0
+
 
     # we create half of the tasks as done
 
     # we create half of the tasks as done
 +
    newtask = TodoTask.create( { 'name': 'New task %03d' % i, 'is_done': is_done } )
 +
    print "created task, id=", newtask.id
  
 +
# self.env.cr.commit()
 +
# to make the changes persistent, uncomment the preceding line,
 +
# that should be the last instruction executed
 
</pre>
 
</pre>
  
==== Uso di metodi definiti all'interno della classe ====
+
=== Uso di metodi definiti all'interno della classe ===
  
 
Questo script fa uso di un metodo, definito all'interno della classe, per eliminare (o, meglio, per contrassegnare come non più attivi) i task marcati come completati:
 
Questo script fa uso di un metodo, definito all'interno della classe, per eliminare (o, meglio, per contrassegnare come non più attivi) i task marcati come completati:
Riga 184: Riga 240:
 
</pre>
 
</pre>
  
==== Recupero di uno o più task in base al loro id ====
+
=== Recupero di uno o più task in base al loro id ===
  
 
Questo script mostra come si possa lavorare su uno o più task partendo dal recupero basato sull'id:
 
Questo script mostra come si possa lavorare su uno o più task partendo dal recupero basato sull'id:
Riga 209: Riga 265:
 
</pre>
 
</pre>
  
==== Attribuzione di un task ad un utente ====
+
=== Attribuzione di un task ad un utente ===
  
 
Questo script permette di attribuire un task ad un particolare utente. È un esempio di come si possano passare dei parametri allo script dalla riga di comando.
 
Questo script permette di attribuire un task ad un particolare utente. È un esempio di come si possano passare dei parametri allo script dalla riga di comando.
Riga 243: Riga 299:
 
     task.user_id=user_id
 
     task.user_id=user_id
 
     print "OK - Variazione utente effettuata"
 
     print "OK - Variazione utente effettuata"
except Error as e:
+
except e:
 
     print "FAIL - Variazione utente fallita"
 
     print "FAIL - Variazione utente fallita"
 
</pre>
 
</pre>

Versione attuale delle 06:17, 2 mag 2017

Istruzioni python eseguite da shell

Può essere comodo, per diversi motivi, eseguire dei comandi python (in maniera interattiva o con degli script) in una shell, interagendo con tutto l'ambiente messo a disposizione da odoo.

Per avere a disposizione una shell in cui eseguire dei comandi, è sufficiente aggiungere il sottocomando shell al comando di avvio. Ad esempio (con riferimento allo script di avvio preparato per gli incontri del LUG dedicati allo sviluppo):

#!/bin/sh

# questo script, odoo_cli.sh, avvia odoo con una shell interattiva
# utilizzando il database tutorial1 e gli addon indicati

ODOOHOME=~/odoo-dev
$ODOOHOME/OCB/odoo-bin shell -d tutorial1  --addons-path=$ODOOHOME/OCB/addons,$ODOOHOME/addons_ita,$ODOOHOME/addons,$ODOOHOME/custom_addons

Persistenza delle modifiche

Attenzione: qualora si apportino delle modifiche ai dati in una shell (in modalità interattiva o con l'esecuzione di uno script), esse non persisteranno nel database, perché viene effettuato un rollback() al termine dell'esecuzione. (Prima del 14 aprile il comportamento non era questo, visto che se la sessione veniva chiusa premendo ctrl-D non veniva effettuato il rollback(). Questo è stato considerato un bug ed è stato corretto.)

È possibile rendere persistenti le modifiche effettuate facendo esplicitamente un commit() con l'istruzione (che deve essere l'ultima ad essere eseguita nello script):

self.env.cr.commit()

Ciò contraddice il principio Never commit the transaction, che però probabilmente vale per il codice scritto per essere eseguito via web, non per gli script da eseguire nella shell. D'altronde, anche il testo di Reis suggerisce di dare questo comando per rendere persistenti le modifiche.

Modalità di lavoro interattiva

Con l'esecuzione di questo script non verrà avviato, come di consueto, il server web interno di odoo, ma si aprirà invece una shell nella quale, in maniera interattiva, sarà possibile eseguire dei comandi.

Type "help", "copyright", "credits" or "license" for more information.
(Console)
>>> self
res.users(1,)
>>> self.name
u'Administrator'
>>> type(self)
<class 'odoo.api.res.users'>
>>> env
<odoo.api.Environment object at 0x7f363f222290>

Nella shell, self è un'istanza del Recordset Model res.users e -- in particolare -- quello con id=1, ossia Administrator.


Esecuzione di script

Per cose più complesse, naturalmente, è possibile preparare degli script da far eseguire alla shell, che quest'ultima leggerà dallo standard input. Ad esempio, supponendo di avere uno script test.py nella directory odooscripts, si potrà digitare il comando

./odoo_cli.sh < odooscripts/test.py

Esempi di script

Qui di seguito descriviamo qualche script per dare l'idea di che cosa si possa fare e come farlo. I sorgenti sono disponibili anche su GitHub.

Visualizzazione dei modelli registrati

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

for model in self.env:
    print model

Nota: è anche possibile ordinare alfabeticamente i modelli con:

for model in sorted(self.env):
    print model

o generare una lista ordinabile con:

models = [i for i in self.env]
models.sort()
for model in models:
    print model

Elenco degli utenti

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

UserModel = self.env['res.users']
# retrieves a Recordset Model for the User class

users = UserModel.search([])
# retrieves all active users

for user in users:
    print "\t".join([str(user.id), user.name ])

Elenco delle voci di configurazione

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

for key in odoo.tools.config.options:
    print key, '=', odoo.tools.config.options[key]


# It is possible to retrieve a specific configuration option, of course:
print
print "Log level:"
print odoo.tools.config.options['log_level']

Elenco degli allegati

Di default, gli allegati vengono registrati nel filesystem in forma di file binario in una sottodirectory della directory indicata nei parametri di configurazione (vedi script precedente) -- ad esempio, in ~/.local/share/Odoo/filestore. Il nome del file è dato dall'sha1sum dello stesso, e i file vengono suddivisi in 256 directory che hanno come nome i primi due caratteri dello stesso sha1sum.

I riferimenti agli allegati sono mantenuti nel database nella tabella ir.attachment. Con questo script li si può trovare come file binari ed associare al loro mimetype e al loro nome originale:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

Attachment = self.env['ir.attachment']
# retrieves a Recordset Model for the Attachment class

for attachment in Attachment.search([('res_model', '!=', False)]):
    print attachment.store_fname, attachment.mimetype, attachment.datas_fname, attachment.file_size, attachment.res_model


print "Files are stored in %s/filestore/%s" % (
    odoo.tools.config.options['data_dir'], odoo.tools.config.options['db_name']
    )

Ricerca e visualizzazione

Da qui in avanti faremo riferimento ad una classe, "TodoTask", descritta nel libro Odoo 10 Development Essentials di Daniel Reis.

Questo script recupera tutti i task con determinate caratteristiche e ne mostra le informazioni rilevanti sullo standard output:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

def print_tasks(tasks):
    for task in tasks:
        print "\t".join([str(task.id), task.name, str(task.is_done) ])
        # prints relevant info of the tasks


TodoTask = self.env['todo.task']
# retrieves a Recordset Model for the TodoTask class

tasks = TodoTask.search([])
# retrieves all active tasks

print "Task attivi (non eliminati)"
print_tasks(tasks)
  
tasks = TodoTask.search([ ('is_done', '=', True) ])
# retrieves current tasks (ie, not marked as done)
# see https://www.odoo.com/documentation/saas-13/reference/orm.html#domains

print "Task correnti (ancora da completare)"
print_tasks(tasks)

tasks = TodoTask.search([ ('active', '=', False) ])
# retrieves deleted tasks (ie, marked as not active)

print "Task eliminati"
print_tasks(tasks)

Creazione di task

Questo script crea una decina di task con nomi caratterizzati da un numero progressivo, contrassegnandoli in maniera alternata come svolti o da svolgere:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

TodoTask = self.env['todo.task']
# retrieves a Recordset Model for the TodoTask class

for i in range(10):
    is_done = i % 2 == 0
    # we create half of the tasks as done
    newtask = TodoTask.create( { 'name': 'New task %03d' % i, 'is_done': is_done } )
    print "created task, id=", newtask.id

# self.env.cr.commit()
# to make the changes persistent, uncomment the preceding line,
# that should be the last instruction executed

Uso di metodi definiti all'interno della classe

Questo script fa uso di un metodo, definito all'interno della classe, per eliminare (o, meglio, per contrassegnare come non più attivi) i task marcati come completati:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

TodoTask = self.env['todo.task']
# retrieves a Recordset Model for the TodoTask class

TodoTask.do_clear_done()
# we call a method defined in our TodoTask class

Recupero di uno o più task in base al loro id

Questo script mostra come si possa lavorare su uno o più task partendo dal recupero basato sull'id:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

TodoTask = self.env['todo.task']
# retrieves a Recordset Model for the TodoTask class

TodoTask.browse(17).do_toggle_done()
# we call a method defined in our TodoTask class on the
# specific task (with id=17)

TodoTask.browse(range(18, 22)).do_toggle_done()
# we call a method defined in our TodoTask class on the
# specific tasks (with id in the range from 18 to 21 included)

Attribuzione di un task ad un utente

Questo script permette di attribuire un task ad un particolare utente. È un esempio di come si possano passare dei parametri allo script dalla riga di comando.

Volendo attribuire al task 4 l'utente 5, ad esempio, si potrà lanciare questo comando:

ODOO_USER_ID=5 ODOO_TASK_ID=4 ./odoo_cli.sh < odooscripts/todotasks_change_user.py

In pratica, vengono valorizzate delle variabili d'ambiente della shell, che poi vengono lette dallo script:

# -*- coding: utf-8 -*-

'''
This script is meant to be executed inside the odoo shell,
not on its own
'''

import os

TodoTask = self.env['todo.task']
# retrieves a Recordset Model for the TodoTask class

user_id = int(os.getenv('ODOO_USER_ID', 0))
print "User: ", user_id
task_id = int(os.getenv('ODOO_TASK_ID', 0))
print "Task: ", task_id

try:
    task = TodoTask.browse(task_id)
    task.user_id=user_id
    print "OK - Variazione utente effettuata"
except e:
    print "FAIL - Variazione utente fallita"