简体   繁体   中英

How to use annotations with z3c.form's DictionaryField

There is documentation on using Python dict with z3c.form (loading & storing form data).

However, the z3c.form datamanager used for dicts is not registered for other types or interfaces (see reference ), whereas annotations typically use something like PersistentDict .

How can I use the DictionaryField datamanager in this scenario? Ie. so that in my form's getContent method I merely return the PersistentDict annotation.

Well, unfortunately there seems no simple solution for this requirement. I once faced the same problem using the datagrid field in a z3c form.

The following instruction solves the problem for the datagrid field, which is a list (PersistentList of dicts (PersistentMappings).

I guess you may adapt this solution for your case.

First you need to add the following code to the getContent method:

from plone.directives import form

class MyForm(form.SchemaEditForm):

    schema = IMyFormSchema
    ignoreContext = False

    def getContent(self):
        annotations = IAnnotations(self.context)
        if ANNOTATION_KEY not in annotations:
            annotations[ANNOTATION_KEY] = PersistentMapping()
        return YourStorageConfig(annotations[ANNOTATION_KEY])

Important note: I wrap the annotation storage to satisfy the get/set behavior of the z3c form. Check the following YourStorageConfig implementation and you will see why :-).

class YourStorageConfig(object):
    implements(IMyFormSchema)

    def __init__(self, storage):
        self.storage = storage

    def __getattr__(self, name):
        if name == 'storage':
            return object.__getattr__(self, name)
        value = self.storage.get(name)
        return value

    def __setattr__(self, name, value):
        if name == 'storage':
            return object.__setattr__(self, name, value)
        if name == 'yourfieldname':
            self.storage[name] = PersistentList(map(PersistentMapping, value))
            return

        raise AttributeError(name)

yourfieldname should be the field name you are using in the form schema.

To implement the a datagrid field, there is some more work to do, but this may be enough for your case.

Please post comments, or tracebacks, so I can provide further help. I'll gonna add more details/explanation if necessary ;-)

It turns out the answer is as easy as the following ZCML adapter registration:

<adapter
  for="persistent.dict.PersistentDict zope.schema.interfaces.IField"
  provides="z3c.form.interfaces.IDataManager"
  factory="z3c.form.datamanager.DictionaryField"
 />

With that, the following customization of a form is sufficient to use ( PersistentDict ) annotations for loading & storing form data:

def getContent(self):
   "return the object the form will manipulate (load from & store to)"
   annotations =  IAnnotations(self.context)
   return annotations[SOME_ANNOTATIONS_KEY_HERE]

This is assuming that a PersistentDict has been previously stored at annotations[SOME_ANNOTATIONS_KEY_HERE] - otherwise the above code will result in KeyError . It would probably be a good idea to change above getContent so that if the annotation does not yet exist, it is created and initialized with some default values.

Finally, note that for some reason, z3c.form warns against enabling DictionaryField for every mapping type, so it may be prudent to for example subclass PersistentDict for form storage, rather than use it directly. I submitted an issue to z3c.form asking for clarification of that warning.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM