Blog

Scopriamo le nuove colonne virtuali: compositeColumn, joinColumn e subQueryColumn

Dalla release 25.04 di Genropy sono stati introdotti importanti aggiornamenti per semplificare e potenziare il lavoro con i database, in particolare con PostgreSQL, l’implementazione che rimane consigliata in ambiente di produzione. Tra le novità più interessanti troviamo delle nuove colonne virtuali, strumenti essenziali per gestire relazioni e query in modo più efficiente. In questo articolo, esploriamo insieme tre nuovi tipi di colonne: compositeColumn, joinColumn e subQueryColumn.

CompositeColumn: La Base per le Chiavi Composte

Le compositeColumn permettono di concatenare più colonne reali, diventando perfette per la gestione delle chiavi primarie composte.

Prendiamo a titolo di esempio la fattura_riga di Sandbox. Non vogliamo specificare solo un prodotto, ma anche un lotto di produzione. Di conseguenza avremo anche una tabella lotto così definita:

class Table(object):
    def config_db(self,pkg):
        tbl=pkg.table('lotto', pkey='key_lotto', name_long='Lotto', 
                      name_plural='Lotti',caption_field='descrizione')
        self.sysFields(tbl, id=False)
        
        tbl.column('prodotto_id',size='22', group='_', name_long='Prodotto'
                    ).relation('prodotto.id', relation_name='lotti', mode='foreignkey', onDelete='raise')
        tbl.column('codice_lotto', size=':10', name_long='Codice Lotto', name_short='Lotto')
        
...
        
        tbl.compositeColumn('key_lotto', columns='prodotto_id,codice_lotto')

Come possiamo notare, la chiave primaria della tabella non è un’id ma una key_lotto composta, costituita dalla combinazione di prodotto_id e codice_lotto.

Sarà quindi la corretta valorizzazione del prodotto e del codice lotto a individuare il lotto corretto per quella specifica riga di acquisto.

Perché usare una chiave composta? Rispetto a una semplice pkey di una tabella, la chiave composta riflette una unicità reale nel dominio: un lotto è identificato univocamente da quel prodotto e da un certo codice. Questo permette di evitare le duplicazioni ma non solo, l’ORM è in grado di riconoscere che si tratta in realtà di due colonne e gestire a quel punto validazioni, indici e join sulle colonne composte.

Vuoi saperne di più?


JoinColumn: valori calcolati sulla base delle relation

Le joinColumn sono colonne virtuali che non si basano su un valore “vero” di partenza, ma su un valore che è desunto dalle condizioni specificate nella relation.

Ad esempio, supponiamo di avere un listino prezzi dove a seconda del tipo di cliente è possibile indicare un prezzo personalizzato per un certo prodotto, a partire da una data iniziale e fino a una data finale. La joinColumn ci permette a questo punto di individuare il prezzo di listino di una riga di vendita di quel prodotto, valida a una certa data:

tbl.joinColumn('prezzo_listino', name_long='Prezzo listino'
                    ).relation('listino.prezzo_personalizzato',
                    cnd="""@prezzo_listino.prodotto_id=$prodotto_id AND @prezzo_listino.cliente_tipo_codice=@fattura_id.@cliente_id.cliente_tipo_codice AND @fattura_id.data BETWEEN @prezzo_listino.data_inizio AND @prezzo_listino.data_fine""")

Il prezzo non necessariamente corrisponderà al prezzo applicato alla riga vendita, ma potrebbe essere utile reperirlo in fase di inserimento per avere un prezzo di default che non sia il prezzo unitario standard del prodotto, ma che tenga conto di quel tipo cliente e del prezzo di listino valido alla specifica data. Invece di una semplice subquery ecco che abbiamo a disposizione uno strumento molto più potente ed efficiente!

Vuoi saperne di più?


SubQueryColumn: Subquery migliorate e con output specifico

Quando lavoriamo con subquery, le subQueryColumn diventano la soluzione ideale. Sono simili alle formulaColumn di tipo select, ma offrono maggiore flessibilità e un output specifico in XML o JSON.

Ad esempio, se vogliamo avere i dettagli relativi ai prodotti venduti in una determinata fattura possiamo fare così:

tbl.subQueryColumn('dettaglio_acquisti', query=dict(table='fatt.fattura_riga', columns="$prodotto_codice,$prezzo_unitario,$quantita", where='$fattura_id=#THIS.id'), mode='json', name_long='Dettaglio acquisti')

Si noti che avremmo potuto ottenere lo stesso risultato con una normale formulaColumn, ma questa colonna virtuale particolare ci offre una visualizzazione tabellare strutturata particolarmente ideale per viste o rappresentazioni grafiche e una sintassi chiara e dedicata.

Vuoi saperne di più?


Relazioni su colonne virtuali

Infine, ma non da ultimo, un’importantissima novità: tutte le colonne virtuali sono da ora in grado di supportare la sintassi della relation. È quindi da oggi possibile impostare relazioni su formulaColumn, aliasColumn, e su tutte le nuove colonne virtuali descritte in questo articolo. Alcuni esempi:

tbl.compositeColumn('lotto_key', columns='prodotto_id,codice_lotto', name_long='Lotto').relation('lotto.key_lotto', mode='foreignkey')

tbl.aliasColumn('anagrafica_id', '@cliente_id.anagrafica_id').relation('er_core.anagrafica.id')

tbl.formulaColumn('listino_valido_id', select=dict(table='fatt.listino', where='$data_fine IS NULL', columns='$id', limit=1)).relation('fatt.listino.id')

Questo permette non solo di sfruttare a pieno i relation_path anche sulle colonne virtuali, ma anche di sfruttare i resolver, di costruire TableHandler utilizzando i relation name delle colonne virtuali, di visualizzare nelle griglie gli alberi anche per le colonne virtuali, di espandere la potenza delle query… insomma, tantissimi nuovi scenari a disposizione!

Puoi vedere le nuove colonne virtuali in azione su Sandbox sul ramo newvirtualcolumns o direttamente con il Docker compose:

docker compose -f sandbox-virtualcolumns-compose.yml up

Utilizzando questo file sandbox-virtualcolumns-compose.yml dopo averlo scaricato.


Cosa ne pensate? Fateci sapere come utilizzerete queste nuove funzionalità nei vostri progetti!