Example of migrating an AT-based content type using blob-enabled FileFields.
Project description
Introduction
The purpose of this example package is to demonstrate how to migrate custom AT-based content types that use FileFields to use plone.app.blob based blobs.
Migrating your own AT-based content types.
In order to migrate your own content types to use blobs you need to perform the following steps:
Use a schema extender to replace the FileField(s) of your content type with BlobField(s). For detailed information on how to do so please look into the archetypes.schemaextender documentation. In essence this breaks down to:
Creating an extension field:
class ExtensionBlobField(ExtensionField, BlobField): """ derivative of blobfield for extending schemas """
Extending your content type to use the blob fields. So for instance if your content type ExampleATType has two fields, namely afile and secondfile you need to register an extender looking like:
class ExampleATTypeExtender(object): adapts(IExampleATType) implements(ISchemaExtender) fields = [ ExtensionBlobField('afile', widget=atapi.FileWidget( label=_(u"A file"), description=_(u"Some file"), ), required=True, validators=('isNonEmptyFile'), ), ExtensionBlobField('secondfile', widget=atapi.FileWidget( label=_(u"Some other file"), description=_(u"Some other file"), ), required=True, validators=('isNonEmptyFile'), ), ] def __init__(self, context): self.context = context def getFields(self): return self.fields
If you want to be able to still use your content type without plone.app.blob on instances that do not support blobs yet, you will find it convenient to register the adapter with a conditional such as:
<adapter zcml:condition="installed plone.app.blob" factory=".exampleattype.ExampleATTypeExtender" />
This way, if plone.app.blob is not installed your original FileField(s) will be used.
Writing a migrator. We use Products.contentmigration to do so, and for the example above the migrator would like this:
class ExampleATTypeMigrator(BaseInlineMigrator): """ example migrator """ src_portal_type = 'ExampleATType' src_meta_type = 'ExampleATType' dst_portal_type = 'ExampleATType' dst_meta_type = 'ExampleATType' fields_map = { 'afile': None, 'secondfile': None, } def migrate_data(self): f = self.obj.getField('afile').get(self.obj) self.obj.getField('afile').getMutator(self.obj)(f) f = self.obj.getField('secondfile').get(self.obj) self.obj.getField('secondfile').getMutator(self.obj)(f) def last_migrate_reindex(self): self.obj.reindexObject()
You will also need to provide a walker iterating over your objects, in this case:
def migrateExampleATTypes(context): portal = getToolByName(context, 'portal_url').getPortalObject() migrator = ExampleATTypeMigrator walker = CustomQueryWalker(portal, migrator, full_transaction=True) savepoint(optimistic=True) walker.go() return walker.getOutput()
You can now call migrateExampleATTypes from a view or a script to convert your objects.
Changelog
1.0 Unreleased
Initial version [ggozad]