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
<http://pypi.python.org/pypi/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
<http://pypi.python.org/pypi/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
<http://pypi.python.org/pypi/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]