Lezione 2: La tabella cliente

In questa lezione vedremo come creare la prima table dei clienti e la pagina per gestirla.


La struttura di un package

Diamo un rapido sguardo a come è strutturato un package. Dopo la sua creazione troveremo all’interno della directory del package stesso (ovvero fatt nel nostro Tutorial), il modulo main.py. In tale modulo viene data la definizione dell’oggetto Python corrispondente al package.

Vediamo che inoltre sono state generate alcune directory:

lib

Viene utilizzata molto raramente e serve a contenere eventuali librerie Python accessorie scritte per essere usate specificamente dal package.

model

Contiene i moduli relativi alla definizione delle tables del database e contiene le customizzazioni delle tables appartenenti ad altri packages.

resources

Contiene le risorse del package. Per risorse si intende un insieme di moduli (javascript, css, Python) che riguarderanno la parte front-end dell’applicazione. Gran parte delle risorse sono suddivise in directory chiamate come le tables del package. In modo tale che per ogni table ci sarà la corrispettiva directory contenente le sue resource di competenza. In particolare troveremo le th_resource, ovvero un tipo particolare di risorsa nella quale si definiscono le viste e le form di una table.

webpages

Contiene i moduli che definiscono alcune pagine realizzate appositamente per il package. Nella pratica comune, per presentare una pagina che consenta l’operatività su una table si usa lavorare sulla th_resource della table, vedremo in seguito come.

Creazione tabella cliente

Procediamo dunque a creare un file cliente.py nella cartella model e a definire in esso una classe Table che eredita da object. La classe Table deve essere vista solo come raccolta di metodi che in seguito saranno mixati alla vera classe della tabella.

class Table(object):

Il metodo config_db riceve come parametro pkg che rappresenta il nostro package.

Definizione della table

def config_db(self, pkg):
    tbl = pkg.table('cliente', pkey='id', name_long='Cliente',
                      name_plural='Cliente', caption_field='ragione_sociale')

Definiamo i parametri principali della tabella:

  • pkey ovvero il campo primary key

  • name_long cioé il nome esteso della tabella al singolare

  • name_plural è al plurale

  • caption_field indica il campo che verrà usato come etichetta del record. Può essere un campo singolo o composto da più campi. In questo caso per rappresentare il cliente usiamo la sua ragione_sociale.

Definizione delle colonne di sistema

Dopo aver definito la table utilizziamo la funzione self.sysFields() che ci permette di aggiungere velocemente alcune colonne di sistema, tra cui la colonna id, che rappresenta la primary key di default per le table dei progetti Genropy. In questo caso, non specificando ulteriori parametri per la funzione self.sysFields() vengono aggiunte:

  • la primary key id

  • il timestamp di inserimento __ins_ts

  • il timestamp di ultima modifica __mod_ts

Nota

La colonna id di sistema è di tipo CHAR lunga 22 caratteri. Le chiavi sono quindi stringhe di 22 caratteri generate casualmente all’inserimento dei record. E” possibile ovviamente specificare altri tipe colonne primary key e ridefinire su ogni table il metodo di genrazione della chiave.

Definizione colonne di cliente

Vediamo ora come si definiscono altre colonne specifiche della nostra table. Cominciando con la ragione_sociale.

tbl.column('ragione_sociale',
            size=':40',
            name_long='Ragione sociale',
            name_short='Rag. Soc.')

Il primo parametro è il nome della colonna. Come secondo parametro abbiamo size, ovvero la lunghezza in caratteri della colonna che in questo caso può variare da 0 a 40.

Nota

Se si desidera specificare esplicitamente il tipo di dato della colonna, bisogna usare il parametro nominato dtype che ammette come valori possibili le sigle di tutti i tipi supportati dai database SQL (I: Ingeger, L:Longinteger, T:Text, etc..). Il tipo di default quando si definisce una colonna è TEXT, a meno che non si utilizzi il parametro size per indicare la lunghezza in caratteri. In tal caso il tipo viene considerato CHAR o VARCHAR. Notiamo che in questo caso è presente un :, ciò indica che la lunghezza può variare da 0 a 40 caratteri e quindi alla colonna viene assegnato il tipo VARCHAR. Se invece il valore fosse stato solamente numerico, la colonna avrebbe avuto tipo CHAR con dimensione pari al valore indicato.

I parametri name_long e name_short si riferiscono a come la colonna verrà etichettata nelle griglie e nelle form.

Quindi vengono poi definite le colonne con il metodo column

  • ragione_sociale

  • indirizzo

  • provincia

  • comune_id

Queste ultime colonne hanno una relazione con le tabelle provincia e comune del package glbl.

Relazioni

Per stabilire una relazione si usa il metodo relation()

provincia.relation('glbl.provincia.sigla',
                         relation_name='clienti',
                         mode='foreignkey',
                         onDelete='raise')

che consente di definire per la relazione il campo collegato, il nome della relazione ed altri parametri. Il primo parametro contiene la colonna in relazione, ovvero sigla della table provincia, del package glbl.

relation_name è come viene nominata la relazione dal punto di vista della table in relazione, in questo caso la provincia. Vale a dire che per la table provincia, seguendo questa relazione ritrovo i clienti appartenenti ad essa.

mode='foreignkey' indica che la relazione viene riportata anche nel database come una foreignkey a tutti gli effetti, con i vincoli che ne conseguono.

Dunque alla fine la definizione della table cliente sarà la seguente:

class Table(object):
    def config_db(self, pkg):
        tbl = pkg.table('cliente', pkey='id', name_long='Cliente',
                      name_plural='Cliente', caption_field='ragione_sociale')
        self.sysFields(tbl)

        tbl.column('ragione_sociale', size=':40', name_long='Ragione sociale', name_short='Rag. Soc.')

        tbl.column('indirizzo', name_long='Indirizzo')
        provincia = tbl.column('provincia', size='2', name_long='Provincia', name_short='Pr.')
        provincia.relation('glbl.provincia.sigla',
                         relation_name='clienti',
                         mode='foreignkey',
                         onDelete='raise')
        tbl.column('comune_id', size='22', group='_',   name_long='Comune').relation('glbl.comune.id',
                                                                                  relation_name='clienti',
                                                                                  mode='foreignkey',
                                                                                  onDelete='raise')

Creazione risorsa th_cliente

Ora lanciamo da console il comando

gnrmkthresource fatturazione:fatt -m

Suggerimento

Questo comando non può essere lanciato dall’interno della directory fatt. Da qualsiasi altro punto funziona, ma non dall’interno della directory del package.

Questo script provvede alla creazione automatica delle risorse th_resource corrispondenti alle tabelle già definite nel model pacchetto fatt. In questo caso solamente cliente. Di norma per ogni tabella si definisce un modulo che ha lo stesso nome della tabella prefissato da th_. Il prefisso th sta per TableHandler che, come vedremo in seguito, è il component che gestisce le pagine per la gestione delle tabelle. In questo caso notiamo che il comando ha generato all” interno della cartella resources/tables/cliente il modulo th_cliente.py.

Con l’opzione -m abbiamo fatto generare anche il modulo menu.py dentro la directory del package. Di questo file parleremo in seguito.

Suggerimento

I menu sono stati profondamente rivisti a inizio 2022, come presentato nell’articolo dedicato ai Nuovi menu sul nostro Blog. Potrebbero quindi esserci delle incongruenze tra le registrazioni video effettuate precedentemente e la documentazione testuale che è stata aggiornata.

Per un approfondimento sul funzionamento delle pagine ottenute definendo le risorse tableHandler vi rimandiamo alla spiegazione fornita dal Manuale utente di Genropy

Classe View

In ogni modulo di tipo th_resource possono essere presenti diverse classi di tipo view. La classe denominata View è quella di default.

Le classi di tipo view sono quelle che implementano il metodo th_struct il quale definisce la vista, ovvero le colonne da visualizzare in una griglia.

Vi sono inoltre altri metodi quali th_order che definisce l’ordinamento di default, e th_query che imposteranno i parametri di default per la query iniziale sulla table.

L’implementazione di th_struct incomincia sempre con la definizione di un elemento rows sul quale si aggiungono gli elementi fieldcell che rappresentano le colonne della table che vogliamo presentare a video, nella nostra griglia.

class View(BaseComponent):

    def th_struct(self, struct):
        r = struct.view().rows()
        r.fieldcell('ragione_sociale')
        r.fieldcell('indirizzo')
        r.fieldcell('provincia')
        r.fieldcell('comune_id')

    def th_order(self):
        return 'ragione_sociale'

    def th_query(self):
        return dict(column='ragione_sociale', op='contains', val='')

Classe Form

In ogni modulo di tipo th_resource possono anche essere presenti diverse classi di tipo form che essenzialmente sono le classi che implementano il metodo th_form.

La classe denominata Form che troviamo già nel file è quella di default. Una classe di form definisce la pagina che ci troviamo di fronte quando l’utente entra in modalità inserimento o modifica di un singolo record.

Nella sua forma più essenziale conterrà la form con i campi di input della tabella a cui si riferisce la risorsa. Ma vedremo che è possibile sviluppare anche layout molto più complessi ed articolati.

class Form(BaseComponent):

  def th_form(self, form):
      pane = form.record
      fb = pane.formbuilder(cols=2, border_spacing='4px')
      fb.field('ragione_sociale')
      fb.field('indirizzo')
      fb.field('provincia')
      fb.field('comune_id')

Il modulo menu.py

Verifichiamo ora come è stato creato il file menu.py che definisce la struttura gerarchica del menu di navigazione dell’applicazione.

Apriamolo e modifichiamolo come segue.

class Menu(object):
  def config(self,root,**kwargs):
    fatt = root.branch(u"Fatturazione")
    fatt.thpage(u"Cliente", table="fatt.cliente")

L’elemento branch crea una sorta di directory, mentre l’elemento thpage crea il collegamento con la pagina standard di gestione di tabella, la quale utilizzerà le classi view e form definite dalla th_resource th_cliente.py

Per ulteriori dettagli si rimanda alla documentazione dedicata ai Menu

Aggiorniamo il database

Dal terminale eseguiamo il comando

gnrdbsetup myfatturazione

Per far sì che automaticamente il database si aggiorni secondo le modifiche stabilite nei file che si trovano nella directory model.

Verifichiamo il risultato

Riavviamo nuovamente il server

gnrwsgiserve myfatturazione

E andiamo a vedere con il browser cosa troviamo nella nostra applicazione. Notiamo subito che nel menu è comparsa la voce cliente nella directory di menu del package fatturazione. Selezionandola viene aperta la pagina cliente e con il bottone + procediamo a creare un record cliente.

La form creata in automatico non è particolarmente evoluta ma consente comunque di caricare i dati del nostro primo cliente. Notiamo che per i campi provincia e comune viene abilitata la ricerca sull’archivio in relazione corrispondente. Il fatto che un campo sia ricercabile è evidenziato dallo sfondo giallo. Inoltre, per la relazione su provincia, è anche abilitata un’icona a freccia per mostrare gli elementi. Per relazioni ad archivi ad alta numerosità, di norma l’icona a freccia viene omessa.

Utilizzando la ricerca sul campo comune notiamo però che tale ricerca non sia limitata ai comuni della provincia selezionata. Se vogliamo restringere la scelta dei comuni alla provincia selezionata, provvediamo ad aggiungere al field del campo comune_id un parametro condition all’interno della classe form definita dalla th_resource th_cliente.py, come mostrato

fb.field('comune_id', condition='$sigla_provincia=:provincia', condition_provincia='^.provincia')

Questo farà sì che nella query degli elementi selezionabili dal widget sia aggiunta la condizione che la provincia del comune sia uguale alla provincia già inserita nel campo provincia.



Allegati: