Dynamically extend Archetypes schemas with named adapters.
Project description
Introduction
This package allows you to modify an Archetypes schema, using simple adapters. This can be used to add new fields, reorder fields and fieldsets or make other changes.
The most common use of schema extension is to allow add-on products to enhance standard Plone content types, for example by adding an option that can be set to toggle special behaviour.
schemaextender hooks into the Archetypes framework by registering an ISchema adapter for BaseContent and BaseFolder, making it responsible for providing the schema for all types derived from those classes. This includes all standard Plone content types. Since only one ISchema adapter can be active schemaextender provides its own mechanism to modify schemas using named adapters.
There are three types of adapters available:
ISchemaExtender: using this adapter you can add new fields to a schema.
IOrderableSchemaExtender: this adapters makes it possible to both add new fields and reorder fields. This is more costly than just adding new fields.
IBrowserLayerAwareExtender: this adpaters are making use of plone.browserlayer, so that the extender is only available if a layer is registered.
ISchemaModifier: this is a low-level hook that allows direct manipulation of the schema. This can be very dangerous and should never be used.
The adapter types are documented in the ‘’interfaces.py’’ file in archetypes.schemaextender.
Simple example
As an example we will add a simple boolean field to the standard Plone document type. First we need to create a field class:
from Products.Archetypes.public import BooleanField from archetypes.schemaextender.field import ExtensionField class MyBooleanField(ExtensionField, BooleanField): """A trivial field."""
schemaextender can not use the standard Archetypes fields directly since those rely on the class generation logic generating accessors and mutator methods. By using the ExtensionField mix-in class we can still use them. Make sure the ExtensionField mix-in comes first, so it properly overwrites the standard methods.
Next we have to create an adapter that will add this field:
from zope.component import adapts from zope.interface import implements from archetypes.schemaextender.interfaces import ISchemaExtender from Products.Archetypes.public import BooleanWidget from Products.ATContentTypes.content.document import ATDocument class PageExtender(object): adapts(ATDocument) implements(ISchemaExtender) fields = [ MyBooleanField("super_power", widget = BooleanWidget( label="This page has super powers")), ] def __init__(self, context): self.context = context def getFields(self): return self.fields
Try to store the fields on the class, that way they aren’t created each time the getFields method gets called. Generally you should make sure getFields does as few things as possible, because it’s called very often.
The final step is registering this adapter with the Zope component architecture. Since we already declared the interface we provide and which type of object we adapt this can be done very quickly in configure.zcml (assuming you put the code above in a file extender.py):
<configure xmlns="http://namespaces.zope.org/zope" xmlns:five="http://namespaces.zope.org/five"> <include package="archetypes.schemaextender" /> <adapter factory=".extender.PageExtender" /> </configure>
Custom fields
If you want you can make more complicated field types as well. The only requirement is that you need to have ExtensionField as the first parent class for your field type. As an example here is a field that toggles a marker interface on an object:
from zope.interface import Interface from zope.interface import alsoProvides from zope.interface import noLongerProvides from Products.Archetypes.public import BooleanField from archetypes.schemaextender.field import ExtensionField def addMarkerInterface(obj, *ifaces): for iface in ifaces: if not iface.providedBy(obj): alsoProvides(obj, iface) def removeMarkerInterface(obj, *ifaces): for iface in ifaces: if iface.providedBy(obj): noLongerProvides(obj, iface) class ISuperPower(Interface): """Marker interface for classes that can do amazing things.""" class InterfaceMarkerField(ExtensionField, BooleanField): def get(self, instance, **kwargs): return ISuperPower.providedBy(instance) def getRaw(self, instance, **kwargs): return ISuperPower.providedBy(instance) def set(self, instance, value, **kwargs): if value: addMarkerInterface(instance, ISuperPower) else: removeMarkerInterface(instance, ISuperPower)
Installation
Installing without buildout
You can install archetypes.schemaextender in either the system python path or in the lib/python directory of your Zope instance. If you have setuptools installed you can do this using easy_install:
easy_install archetypes.schemaextender
If you do not have setuptools you can install it manually using the setup.py script in the package source. If you want to install inside your Zope instance instead of system wide you can its ‘’–prefix=’’ option to install in the ‘’lib/python’’ directory of your Zope instance.
After installing the package it needs to be registered in your Zope instance. This can be done by putting a archetypes.schemaextender-configure.zcml file in the etc/package-includes directory with this content:
<include package="archetypes.schemaextender" />
or, alternatively, you can add that line to the configure.zcml in a package or Product that is already registered.
Installing with buildout
If you are using buildout to manage your instance installing archetypes.schemaextender is even simpler. You can install it by adding it to the eggs line for your instance:
[instance] eggs = archetypes.schemaextender zcml = archetypes.schemaextender
The last line tells buildout to generate a zcml snippet that tells Zope to configure archetypes.schemaextender.
If another package depends on the archetypes.schemaextender egg or includes its zcml directly you do not need to specify anything in the buildout configuration: buildout will detect this automatically.
After updating the configuration you need to run the ‘’bin/buildout’’, which will take care of updating your system.
Changelog
1.0 - 2008-07-17
No changes since 1.0rc1.
1.0rc1 - 2008-04-07
Added optional plone.browserlayer support. Extenders implementing IBrowserLayerAwareExtender need to have a layer attribute. Those extenders are taken into account only if the specified layer is active. [jensens] 2008-03-03
1.0b1 - 2007-12-07
Schema modifiers implementing ISchemaModifier are now responsible for copying fields they modify. See README and the doc strings. [fschulze]
Added a simple benchmark and made some optimizations by avoiding a lot of field copying. [fschulze, wiggy]
Use a marker interface instead of overrides.zcml - this means you don’t need to muck with overrides in dependent products. [optilude]
Added code to allow addition of new schemata. We need an ordered dictionary to not bork the order of the schemata. [jensens]
Add a small benchmark utility. [wichert]
Replace the high-level test with unit-tests and extend the test coverage. [wichert]
Rewrite the README to be more human readable. [wichert]
1.0a1 - 2007-10-15
First public release.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Hashes for archetypes.schemaextender-1.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8f95090744b3fc4fe86702d76f86397611651eebffc3979d53c83fb2fff7177a |
|
MD5 | a8915df30933d7e245d82d8da53aed0d |
|
BLAKE2b-256 | 6d51aa04e235939215aa1ffadc3d916509d1bdbbc33fc2af6be5c790b678ee51 |