I need to add an attribute for every attribute in an interface. So I am trying to dynamically modify it to add them, but not with much success for now.
Let's say I have the following interface:
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
And I would like to modify it like that:
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
visbility_first_name = schema.Bool(title=u'Display: first name')
visbility_last_name = schema.Bool(title=u'Display: last name')
I tried modifying the class afterwards, but as it was already initialized, the schema was set and I was not sure how to change it. I also thought about writing a directive (e.g.: interface.Implements()) but it seems quite complicated to do just to add attributes.
My final goal is to add a z3c.form fieldset with a set of Bool widgets.
So, is there a way to do it in Python, or do I have to modify the interface and add all the attributes manually ?
Thanks !
You can create a dynamic subclass of the interface using the InterfaceClass
metatype.
Create a dictionary of the additional schema fields:
fields = {}
for name, attr in IMember.namesAndDescriptions():
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
Now you can create a dynamic interface subclassing your existing interface:
from zope.interface.interface import InterfaceClass
IMemberExtended = InterfaceClass('IMemberExtended', (IMember,), fields)
This can all be wrapped up in a class decorator if you so desire:
from zope.interface.interface import InterfaceClass
from zope import schema
def add_visibility_fields(iface):
fields = {}
for name, attr in iface.namesAndDescriptions():
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
return InterfaceClass(iface.__name__, (iface,), fields)
which you'd use on your existing interface:
@add_visibility_fields
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
This creates a subclass; you can also replace the whole interface with the generated interface:
def add_visibility_fields(iface):
fields = {}
for name, attr in iface.namesAndDescriptions():
fields[name] = attr
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
return InterfaceClass(iface.__name__, iface.__bases__, fields)
Demo of that last version:
>>> @add_visibility_fields
... class IMember(Interface):
... first_name = schema.TextLine(title=u'first name')
... last_name = schema.TextLine(title=u'last name')
...
>>> IMember.names()
['visible_last_name', 'first_name', 'last_name', 'visible_first_name']