Search code examples
pythonplonezopezodb

Plone/Zope/ZODB: how to override storage method on a File field


I have a File Field in my Plone product that I want to allow the user to "Turn Off" blob storage. The file will be stored elsewhere. I can't seem to do it.

Below is my attempt. I can't get Products.Archetypes.Field.ObjectField.setStorage() to recognize that this 'noStorage' instance "provides" IStorage.

Much inconsequential code has been removed for brevity, but the complete source can be found at https://github.com/flipmcf/rfa.kaltura


The Archetype schema includes "ATBlob.schema" to pull in the "File" field, then:

class KalturaVideo(ATBlob, KalturaBase.KalturaContentMixin):

    nostorage = NoStorage()
    directlyProvides(nostorage, INoStorage)

    def __init__(self, oid, **kwargs):
        if settings.storageMethod == u"No Local Storage":
            import pdb; pdb.set_trace() # 
            #(Pdb) IStorage.providedBy(self.nostorage)
            #True
            self.getField('file').setStorage(self, self.nostorage)

My Storage class and interface is really boring:

from zope.interface import implements
from ZODB.interfaces import IStorage
from zope.component.zcml import interface
from zope.interface import Interface

class INoStorage(IStorage):
    pass 

class NoStorage(object):
    """Completely skip storage on Plone."""
    implements(INoStorage)

    def __init__(self):
        pass 

    def close():
        pass

    def getName():
        return "NoStorage - Blackhole"

    #etc... lots of implemented methods that do nothing.

configure.zcml in the 'storage' package also:

<adapter
   factory=".storage.NoStorage"
   provides=".storage.INoStorage"
   for="Products.Archetypes.interfaces.field.IObjectField"
/>

<adapter
   factory=".storage.NoStorage"
   provides=".storage.IStorage"
   for="Products.Archetypes.interfaces.field.IObjectField"
/>

Now, within the setStorage() method of Products.Archetypes.Field.ObjectField:

def setStorage(self, instance, storage):
    import pdb; pdb.set_trace()
    #(Pdb) IStorage.providedBy(storage)
    #False
    #HeadDesk
    if not IStorage.providedBy(storage):
        raise ObjectFieldException, "Not a valid Storage method"

And when I debug, IStorage.providedBy(storage) returns False

Why would it return False in setStorage and True in the calling code? Am I not registering the interface correctly?


Solution

  • Note that in the module Products.Archetypes.Field, the IStorage instance there is actually sourced from this:

    from Products.Archetypes.interfaces.storage import IStorage
    

    Comparing the interface resolution order (via __iro__) we get

    >>> from pprint import pprint as pp
    >>> pp(ZODB.interfaces.IStorage.__iro__)
    (<InterfaceClass ZODB.interfaces.IStorage>,
     <InterfaceClass zope.interface.Interface>)
    >>> pp(Products.Archetypes.interfaces.storage.IStorage.__iro__)
    (<InterfaceClass Products.Archetypes.interfaces.storage.IStorage>,
     <InterfaceClass zope.interface.Interface>)
    

    As the INoStorage was subclassed from ZODB.interfaces.IStorage, and that interface class isn't the parent of Products.Archetypes.interfaces.storage.IStorage which the setStorage calls providedBy on, the NoStorage class as define will not satisfy that check. To solve this, just have INoStorage simply inherit from the Archetype version of the IStorage and implement all its methods and it should work as intend.

    That said, you could simply your code somewhat further with regards to the way you provide the interfaces, see this example:

    >>> class INoStorage(Products.Archetypes.interfaces.storage.IStorage):
    ...     pass
    ... 
    >>> class NoStorage(object):
    ...     zope.interface.implements(INoStorage)
    ... 
    >>> nostorage = NoStorage()
    >>> Products.Archetypes.interfaces.storage.IStorage.providedBy(nostorage)
    True
    

    Inheritances of the Interface subclasses will persist correctly through without extra directlyProvides definitions, so you can just drop that extra call inside the KalturaVideo class definition. Naturally, you can just do from ... import IStorage and simply that to class INoStorage(IStorage). The example was done so to make things more explicitly visible.