Personal tools
Trasformiamo i nostri content-type in modo semplice con Products.contentmigration

Cambiare forma non è mai stato così facile

Nov 05, 2012

Trasformiamo i nostri content-type in modo semplice con Products.contentmigration

Breve guida pratica su come utilizzare Products.contentmigration per migrare i contenuti di un sito da un content-type a un altro senza diventare matti

Negli ultimi mesi abbiamo dovuto migrare una serie di portali dal vecchio Plone 3.3.5 alla nuova release 4.2.
Questa operazione è anche stata l'occasione per fare un po' di rifattorizzazione dei prodotti (necessaria in alcuni casi, per farli funzionare anche su Plone 4) e di pulizia generale dei contenuti.

"Quei content-type sono vecchi... cambiamoli!"

Su alcuni portali avevamo una serie di content-type creati ad hoc per esigenze passate, poi diventati obsoleti o perché le funzionalità sono state implementate (meglio) in altri prodotti, o perché non abbiamo più bisogno di loro.
Per esempio, avevamo creato diversi content-type che aggiungevano funzionalità a quelli base di Plone (al tempo non conoscevamo ancora la potenza di archetypes.schemaextender) sovrascrivendoli, e ora dovevamo tornare indietro per poter ricominciare a utilizzare le versioni base.

shape boxTornare a utilizzare le versioni standard è semplice, basta disinstallare il nostro prodotto.
Cosa fare però con i contenuti già creati? Come torniamo a una situazione "standard" senza rompere niente?
E' come avere tra le mani una serie di cubi avendo a disposizione solo buchi tondi: non funzioneranno più, ma non li possiamo perdere. Vanno sostituiti.

La soluzione è facile: li migriamo!

 

Per sostituire i vecchi content-type abbiamo utilizzato un prodotto chiamato Products.contentmigration, un framework generico che aiuta a creare facilmente una migrazione tra tipi diversi.

Vediamo ora come creare una procedura di migrazione.

Definizione della procedura

def makeTypeMigrator(context, src_type, dst_type):
    """ generate a migrator for the given at-based portal type """
    from Products.contentmigration.archetypes import InplaceATItemMigrator
class ATItemMigrator(InplaceATItemMigrator): src_portal_type = src_type src_meta_type = src_type dst_portal_type = dst_type dst_meta_type = dst_type
return ATFolderMigrator
def migrateOldContentType(context): from Products.contentmigration.walker import CustomQueryWalker migrator = makeTypeMigrator(context, 'OldPortalType', 'NewPortalType') walker = CustomQueryWalker(context, migrator, src_portal_type="OldPortalType", dst_portal_type="NewPortalType", use_savepoint=True, query={'path': '/siteid/folder-1'}) walker.go() return walker.getOutput().splitlines()

In questo esempio abbiamo un metodo "makeTypeMigrator" che, quando viene chiamato, crea una classe che eredita da InplaceATItemMigrator (classe che permette di rimpiazzare il vecchio content-type con il nuovo, sostituendolo in tutto e per tutto) alla quale serve sapere solamente qual'è il tipo di origine e quello di destinazione.

Il metodo "migrateOldContentType", invece, è quello che si occupa di preparare e svolgere a tutti gli effetti la migrazione.
Per effettuare la migrazione servono 2 cose: il migrator (oggetto che si occupa di tutte le operazioni di migrazione) e un walker. Il walker serve per navigare attraverso il catalogo ed effettuare la migrazione ad ogni elemento che trova.

Come si può vedere, gli vengono passati diversi parametri come il migrator, appunto (per la procedura di migrazione) e i vari content-type (quello di partenza viene utilizzato per fare una ricerca in catalogo, se non si passano ulteriori parametri).

Ci sono poi diversi altri parametri opzionali, per esempio la possibilità di usare dei savepoint e di definire una query per la ricerca in catalogo più sofisticata.
Si può anche impostare un metodo da richiamare prima di migrare ogni risultato trovato (attraverso l'attributo "callBefore") per fare alcune operazioni o controlli ulteriori (se questo metodo ritorna False, il contenuto non viene migrato).

A questo punto, basta solamente richiamare il metodo "migrateOldContentType" per lanciare la procedura. Questo ci ritorna le informazioni di tutte le operazioni effettuate, quindi è anche possibile mostrarle all'utente (stampandole nel log, per esempio).

messages = migrateOldContentType(portal)
for m in messages:
    logger.info(m)

Funzioni aggiuntive

Operazioni post-migrazione

Come abbiamo visto, con l'attributo callBefore è possibile eseguire operazioni prima della migrazione. Ma è anche possibile eseguire operazioni appena dopo la migrazione.
Per fare questo, basta creare dei metodi dentro alla classe ATItemMigrator i cui nomi inizino con "last_migrate_".
Questi metodi verranno richiamati una volta completata la migrazione di un contenuto, e si avranno a disposizione sia il vecchio oggetto (self.old) che quello nuovo (self.new).

Migrazione di oggetti folderish

L'esempio di prima era riferito ad Archetypes semplici. Se si vogliono migrare degli oggetti folderish bisogna utilizzare la classe InplaceATFolderMigrator.

Nota Bene: nelle versioni compatibili per Plone 3.3.5 (fino alla 2.0.1), la migrazione delle cartelle ha dei problemi e non funziona correttamente.

Migrazione di campi

E' anche possibile effettuare una migrazione di campi all'interno di un Archetype. Ciò è utile, per esempio, quando si deve cambiare il nome, lo storage o effettuare delle modifiche ai valori memorizzati.
Per usare questa funzionalità basta utilizzare la classe InlineFieldActionMigrator.

Conclusioni

In conclusione, questo è un ottimo tool che permette di effettuare operazioni complesse scrivendo pochissime righe di codice.

Come abbiamo visto, ha anche parecchie funzioni che permettono di creare procedure altamente personalizzate in base ai propri bisogni.

Filed under: , , ,
comments powered by Disqus