Introduzione
Lo scopo di questo breve tutorial è illustrare il processo di sviluppo utilizzando il Framework PyPaPi. Verrà preso in considerazione inizialmente il caso più semplice, che mano a mano estenderemo.
Caso base: una tabella
In questo primo passo andreamo a considerare un'unica tabella: una biblioteca. Per le nostre prove utilizziamo per ora lo schema anagrafiche già presente nella Suite PyPaPi.
1. Definizione in Postgres
Le definizioni della base dati e la loro documentazione risiedono nella cartella docs. In database si trovano le tabelle definite per Postresql, mentre in oracle quelle di Oracle.
Per aggiungere una tabella basterà aggiungere un file nello schema corrispondente, nel nostro caso docs/database/tabelle/anagrafiche/libri.rst. La prima parte del file riporta generalmente la documentazione di tabella e campi.
.. -*- mode: rst; coding: utf-8 -*-
.. :Progetto: PyPaPi -- Anagrafica Libri
.. :Creato: gio 22 ott 2009
.. :Autore: Tiziano Lattisi <tiziano@axiastudio.it>
.. :Licenza: GNU General Public License version 3 or later
..
Anagrafiche.Libri
-----------------
La tabella Libri costituisce un piccolo esempio di sviluppo con il Framework PyPaPi.
.. argomento:: Informazioni sui singoli campi
IDLibro
Identificativo univoco del libro (progressivo tramite generatore)
Titolo
Titolo del libro
ISBN
Codice ISBN
Poiché utilizzeremo un generatore per la chiave primaria, andiamo a definirlo.
.. script:: Generatore Anagrafiche.IDLibro :language: sql :depends: Anagrafiche create sequence Anagrafiche.Gen_IDLibro
E' il momento della definizione vera e propria della tabella; i tipi di dato definiti sul Framework PyPaPi (estendibili) si possono trovare in domini.rst: potrebbe essere ad esempio interessante creare un tipo di dato specifico per il codice ISBN...
.. script:: Anagrafiche.Libri
:language: sql
:depends: TimeStamped, Anagrafiche
create table Anagrafiche.Libri
(
IDLibro smallid_t not null,
Titolo longstring_t,
ISBN longstring_t,
constraint PK_Libri primary key (IDLibro)
)
Scriviamo un semplice trigger per la gestione dell'inizializzazione del record; se la PrimaryKey? viene passata come NULL o 0, allora la si imposterà come da generatore. Questo trigger ha lo scopo di mantenere aperta la possibilità di inserire anche record specificando una PK, nonché come base per future operazioni da compiere all'atto dell'inserimento del record.
.. argomento:: Inserimento e aggiornamento dei record
.. script:: Inserimento record Anagrafiche.Libri
:language: sql
:depends: Anagrafiche.Libri
create function Anagrafiche.inizializza_libri()
returns trigger as '
begin
if new.IDLibro is NULL or new.IDLibro = 0 then
new.IDLibro = nextval(''Anagrafiche.Gen_IDLibro'');
end if;
return new;
end;
' language plpgsql;
create trigger trg_ins_ts_init_libri
before insert on Anagrafiche.Libri
for each row execute procedure Anagrafiche.inizializza_libri();
Permettiamo le operazioni base sulla tabella a tutti gli utenti.
.. argomento:: Permessi di accesso
.. script:: Permessi di accesso a Anagrafiche.Libri
:language: sql
:depends: Anagrafiche.Libri
grant all privileges on Anagrafiche.Libri to group amministratore
;;
grant select, insert, update, delete on Anagrafiche.Libri to public
;;
grant all privileges on Anagrafiche.Gen_IDLibro to public
A questo punto è necessario includere il file libri.rst affinché sia preso in considerazione all'atto della generazione del database e della documentazione; lo facciamo aggiungendo una direttiva include in fondo al file docs/database/tabelle/anagrafiche.rst.
.. include:: anagrafiche/libri.rst
La definizione della tabella è completata, e possiamo testare la sua creazione lanciando, da docs/database, il comando
$ make scratch
Il database verrà cancellato e rigenerato, al termine possiamo verificare l'avvenuta creazione della tabella tramite psql.
$ psql pypapi -c 'select * from anagrafiche.libri;' idlibro | titolo | isbn ---------+--------+------ (0 rows)
2. Dichiarazione tabella in SQLAlchemy
In questo semplice caso la tabella Postgresql viene vista a livello dell'ORM in maniera lineare, quindi la sua dichiarazione la rispecchierà a grandi linee. I file da modificare si trova in questo caso in lib/pypapi/db/model/tables/anagrafiche.py, visto che abbiamo deciso di sfruttare lo schema Anagrafiche preesistente.
libri = Table('libri', metadata,
Column('idlibro', smallid_t,
Sequence('gen_idlibro', schema='anagrafiche'),
primary_key=True),
Column('titolo', longstring_t),
Column('isbn', longstring_t),
schema='anagrafiche'
)
E' necessario aggiungere la tabella nella lista __all__ dei contenuti importabili dal modulo anagrafiche.py, nelle ultime righe del file.
__all__ = [ ... 'libri' ]
3. Definizione dell'interfaccia ILibro
I contenuti della tabella libri saranno, si suppone libri; andiamo a definire in un'interfaccia il comportamento degli oggetti di tipo libro. Ancora una volta, vista la semplicità dell'esempio, la definizione ricalcherà quella della tabella (ci sarà tempo per complicare l'esempio). Il file da modificare questa volta si trova in lib/pypapi/db/interfaces/anagrafiche.py.
class ILibro(IEntitaBase):
idlibro = schema.Int(title=u"ID Libro",
description=u"Identificativo e chiave primaria del libro",)
titolo = schema.TextLine(title=u"Titolo",
description=u"Titolo di copertina del libro",)
isbn = schema.TextLine(title=u"ISBN",
description=u"Codice ISBN",)
4. Creazione della class Libro
Finalmente andiamo a creare la classe Libro, che per il momento sarà niente più che un marcaposto. Questa volta dobbiamo aggiungere un file nuovo lib/pypapi/db/model/entities/anagrafiche/libro.py.
# -*- mode: python-mode; coding: utf-8 -*-
# :Progetto: PyPaPi -- Libro
# :Creato: ven 23 ott 2009
# :Autore: Tiziano Lattisi <tiziano@axiastudio.it>
# :Licenza: GNU General Public License version 3 or later
#
import zope.interface
from pypapi.db.model.entities.base import EntitaBase
from pypapi.db.interfaces import ILibro
class Libro(EntitaBase):
zope.interface.implements(ILibro)
def getCaption(self):
return '%s (%s)' % (self.titolo, self.isbn)
Unico metodo definito è getCaption, che si occupa di definire la rappresentazione in modalità etichetta che l'oggetto libro avrà: in questo caso il titolo del libro con, tra parentesi, il codice ISBN.
In fondo a lib/pypapi/db/model/entities/anagrafiche/__init__.py aggiungiamo la seguente riga, per fare in modo che Libro possa essere importato direttamente da anagrafiche, senza dover specificare il modulo libro.
from libro import Libro
Includiamo infine Libro nella lista __all__ di lib/pypapi/db/model/entities/__init__.py.
__all__ = [
...
# Anagrafiche
...
'Libro',
...
]
5. Mappatura tra l'oggetto e la tabella
Tramite una definizione nel file lib/pypapi/db/model/mappers.py dichiariamo che le righe nella tabella libri sono oggetti di tipo Libro. Qui possiamo notare l'uso semplificato di entities.Libro anziché entities.libro.Libro (vedi sopra). La definizione è molto semplice in questo caso base.
mapper(entities.Libro, tables.libri)
Test intermedio
Possiamo finalmente fare un piccolo test di funzionamento di quanto scritto. Da console avviamo un interprete python.
$ python Python 2.6.2 (release26-maint, Apr 19 2009, 01:58:18) [GCC 4.3.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from pypapi.db.db import createDatabase >>> from pypapi.db.model.entities import Libro >>> db = createDatabase() >>> db.open() >>> mat = Libro(titolo=u'Il matematico indiano', isbn=u'9788804580034') >>> db.session.add(mat) >>> db.session.flush()
Abbiamo creato un nuovo oggetto di tipo libro, l'abbiamo inserito nella sessione di lavoro del database appena aperto, e abbiamo salvato tutto. Possiamo verificare l'avvenuto in Postgresql.
$ psql pypapi -c 'select * from anagrafiche.libri;'
idlibro | titolo | isbn
---------+-----------------------+---------------
1 | Il matematico indiano | 9788804580034
(1 riga)
:-)
6. Form QTDesigner
Le form del Framework PyPaPi sono disegnate con il QTDesigner di Trolltech, che produce dei file di estensione ui che possono venir caricati a runtime. Poiché in questo esempio intendiamo semplicemente agganciare la nostra nuova form di anagrafica libri in un'applicazione preesistente (Protocollo e gestione dati amministrativi), la cartella su cui andiamo a lavorare sarà lib/pypapi/app/protocollo. In particolare la form che andremo a costruire con QTDesigner sarà salvata in lib/pypapi/app/protocollo/form/ui/libri.ui.
Apriamo quindi il designer, e cominciamo a disegnare. Quando ci viene chiesto che tipo di form disegnare, scegliamo dai template Main Window, per creare un form basato su QMainWindow.
Dobbiamo rimuovere la barra dei menù e quella di stato, perché della loro creazione e gestione si occuperà direttamente il Framework, quindi: tasto destro sulla prima e Remove Menu Bar, tasto destro sulla seconda e Remove Status Bar.
Disegnamo quindi la nostra semplice form trascinando dal Widget Box di sinistra gli oggetti di tipo Label e Line Edit. Il Framework prevede l'utilizzo di Widget personalizzati molto più complessi, ma ci sarà tempo per approfondire.
Per il Line Edit relativo al titolo del libro, imposto l'objectName (dal Property Editor a destra) a titolo, e creo una Dinamyc Property
con nome column,
valorizzata sempre a titolo.
Seguo lo spesso procedimento per il codice isbn.
7. Codice di gestione della form
I comportamenti specifici della form Libri vengono impostati nel nuovo file lib/pypapi/app/protocollo/form/libri.py:
# -*- mode: python-mode; coding: utf-8 -*-
# :Progetto: PyPaPi -- form libri
# :Creato: 23 ott 2009
# :Autore: Tiziano Lattisi <tiziano@axiastudio.it>
# :Licenza: GNU General Public License version 3 or later
#
from pypapi.app.forms import PyPaPiForm
class Libri(PyPaPiForm):
pass
Per ora, come si può vedere, nessun comportamento specifico è stato inserito, ma verrà tutto ereditato da PyPaPiForm?.
Applico un giochetto simile a quello per le entities, per poter importare la form Libri senza specificare il modulo libri, aggiungendo in fondo a lib/pypapi/app/protocollo/form/__ini__.py:
from libri import Libri
8. Aggancio del form all'applicazione
Per il nostro primo piccolo esempio non creiamo una nuova applicazione MDI, ma piuttosto agganciamo questa form all'applicazione preesistente configurata in lib/pypapi/app/protocollo.conf, aggiungendo prima della chiusura del tag <application> la nostra form.
<form Libri>
class pypapi.app.protocollo.form.Libri
interface pypapi.db.interfaces.ILibro
uifile ui/libri.ui
title Anagrafica libri
icon :/images/costanti.png
toplevel True
<entity .>
<column titolo/>
<column isbn/>
</entity>
</form>
</application>
Come si vede qui vengono uniti i vari pezzi sopra definiti: la form Libri, l'interfaccia ILibro degli oggetti che si comportano da libri (ad esempio l'oggetto Libro), la UI libri.ui. Impostiamo un titolo per la nostra form, e un'icona da utilizzare per richiamarla dalla toolbar principale (ne sfruttiamo una preesistente). La configurazione dell'entità . (punto) si riferisce alle colonne dell'oggetto stesso che devono essere prese in considerazione per popolare la form; si vedrà più avanti come questa configurazione si complica con l'introduzione della relazionalità.
Lancio dell'applicazione
Posso eseguire al solito modo, dalla cartella apps:
$ python ppp.py -c ../lib/pypapi/app/protocollo.conf
Poiché non abbiamo lanciato ancora un make test dobbiamo accedere con l'unico utente generato da make scratch ovvero admin con password pypapi.
Tramite il nuovo pulsante omonimo possiamo aprire la finestra Libri.
Bene, ecco tutto.
Bibliografia propedeutica
Note
- Oracle non è allo stato attuale completamente allineato su tutta la Suite PyPaPi: il database di riferimento è Postgresql, mentre Oracle viene aggiornato e allineato su necessità. Anche la documentazione è unicamente riportata sul lato Postgresql.
Attachments
- newform.png (30.3 kB) - added by tiziano 11 months ago.
- removemenubar.png (11.6 kB) - added by tiziano 11 months ago.
- designer_formlibri.png (6.5 kB) - added by tiziano 11 months ago.
- objectname.png (32.6 kB) - added by tiziano 11 months ago.
- addpropertycolumn.png (11.9 kB) - added by tiziano 11 months ago.
- propertycolumn.png (10.4 kB) - added by tiziano 11 months ago.
- formlibri.png (39.5 kB) - added by tiziano 10 months ago.







