.. _orm_genropy/virtual_columns/compositecolumn: compositeColumn =============== Le ``compositeColumn`` sono colonne che rappresentano la **concatenazione** di altre colonne, e sono per questo la base per la generazione di chiavi primarie composte. Esempio:: 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.column('descrizione', name_long='Descrizione') tbl.column('data_produzione', dtype='D', name_long='Data produzione') tbl.column('data_scadenza', dtype='D', name_long='Data scadenza') tbl.compositeColumn('key_lotto', columns='prodotto_id,codice_lotto', static=True) 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*. .. hint:: Si noti che non viene specificata una *size* né un *dtype*, essendo composta da elementi di tipo e dimensioni differenti A questo punto nella *fattura_riga* avremo:: tbl.column('prodotto_id',size='22' ,group='_',name_long='!![it]Prodotto' ).relation('prodotto.id',relation_name='righe_fattura', mode='foreignkey',onDelete='raise') tbl.column('codice_lotto', size=':10', name_long='Lotto') tbl.compositeColumn('lotto_key', columns='prodotto_id,codice_lotto' ).relation('lotto.key_lotto', mode='foreignkey') Anche qui abbiamo quindi un'altra compositeColumn, in :ref:`relazione` con la tabella *lotto*. E nell’interfaccia? Aggiorniamo la *ViewFromFattura* di *fattura_riga*:: class ViewFromFattura(BaseComponent): def th_struct(self,struct): r = struct.view().rows() r.fieldcell('_row_count',counter=True,hidden=True) r.fieldcell('prodotto_id',edit=dict(validate_notnull=True)) r.fieldcell('codice_lotto',edit=dict(table='fatt.lotto', tag='dbSelect', condition='$prodotto_id=:prd_id', condition_prd_id='=#ROW.prodotto_id', hasDownArrow=True, alternatePkey='codice_lotto'), width='8em') r.fieldcell('quantita',edit=dict(validate_notnull=True),width='7em') r.fieldcell('prezzo_unitario') r.fieldcell('aliquota_iva') r.fieldcell('prezzo_totale',totalize='.totale_lordo',formula='quantita*prezzo_unitario') r.fieldcell('iva',totalize='.totale_iva',formula='aliquota_iva*prezzo_totale/100') r.fieldcell('@lotto_key.data_scadenza', width='6em', name='Scad.') In sostanza, il campo ``codice_lotto`` sarà condizionato a un lotto presente per quello specifico *prodotto_id*. L'individuazione contestuale dei due campi per quel record permetterà di valorizzare correttamente la *compositeColumn* che abbiamo costruito. I benefici di una chiave composta --------------------------------- L'uso della chiave composta, rispetto a una normale *pkey* di una tabella di relazione: - **Riflette una unicità reale nel dominio**: un lotto è identificato univocamente da quel prodotto e da un certo codice. - **Evita duplicazioni**: non è possibile avere due lotti con stesso *codice_lotto* per lo stesso *prodotto_id*. - L’univocità è garantita a **livello logico e strutturale**, non affidata a una convenzione. Rispetto a una normale *pkey*, insomma, l'ORM di Genropy è in grado di riconoscere che è costituita da due colonne e può usarla come vera chiave primaria. Viene inoltre salvata come JSON array, ed è quindi leggibile, esportabile, e supportata pienamente dal model per gestire validazioni, indici e join su colonne composite. .. raw:: html
**Parametri:** +------------------------+------+--------------------------------------------------+ | Nome parametro | Tipo | Descrizione | +========================+======+==================================================+ |columns |T |Permette di specificare le colonne da concatenare,| | | |come stringa di valori separati da virgola (es: | | | |columns='prodotto_id,codice_lotto') | +------------------------+------+--------------------------------------------------+ .. sectionauthor:: Davide Paci