Search code examples
pythonplonearchetypes

Why do the return value of the first and second calls of image field accessor differ?


The first call of the accessor of the image field of a BlobWrapper returns None and the second call returns a BlobWrapper. Some hint?

In [1]: id = app.Plone.invokeFactory('Image', 'myimage', title='MyImage')

In [2]: image=app.Plone[id]

In [3]: blobWrapper=image.getBlobWrapper()

In [4]: accessor=blobWrapper.getField('image').getAccessor(blobWrapper)

In [5]: accessor()

In [6]: accessor()
Out[6]: <plone.app.blob.field.BlobWrapper at 0xa35a194>

Solution

  • The getAccessor method of the field takes the object in this case your ATImage instance as parameter not a blobWrapper instance

    >>> accessor = image.getField('image').getAccessor(image)
    >>> accessor()
    <plone.app.blob.field.BlobWrapper object at 0x1050446e0>
    

    Check this github link

    security.declarePublic('getAccessor')
    def getAccessor(self, instance):
        """Return the accessor method for getting data out of this
        field"""
        if self.accessor:
            return getattr(instance, self.accessor, None)
        return None
    

    UPDATE: Explenation why the first call of the accessor is None and second not.

    I has something to do with the magic's of Products.Archetypes. If you pass the BlobWrapper instance instead of a ATImage instance, the following happens:

    First call: At some point the get method of Archetypes ObjectField gets called, See https://github.com/plone/Products.Archetypes/blob/master/Products/Archetypes/Field.py#L785

    It catches the AttributeError (AttributeError: Attribut...'image'), which btw. usually happens if you add a new Field to a ContentType, because the BlobWrapper instance has no Attribute image (The ATImage would have this attribute).

    Then it initializes your BlobWrapper like a new field on the ATImage. This persists some crap on your Blobwrapper instance, which you don't want :-) The return value is None, see https://github.com/plone/Products.Archetypes/blob/master/Products/Archetypes/Field.py#L803

    Second call: Since the first call did some modifications to your BlobWrapper instance, the second call works and returns the correct value. https://github.com/plone/Products.Archetypes/blob/master/Products/Archetypes/Field.py#L785

    Technically the first call initializes an AnnotationStorage on your BlobWrapper instance.

    > /Users/maethu/.buildout/eggs-cache/Products.Archetypes-1.9.4-py2.7.egg/Products/Archetypes/Storage/annotation.py(62)get()
    -> def get(self, name, instance, **kwargs):
    (Pdb) l
     57             """Clean up data in set method
     58             """
     59             raise NotImplementedError
     60
     61         security.declarePrivate('get')
     62  ->     def get(self, name, instance, **kwargs):
     63             ann = getAnnotation(instance)
     64             value = ann.getSubkey(self._key, subkey=name, default=_marker)
     65             if value is _marker:
     66                 if self._migrate:
     67                     value = self._migration(name, instance, **kwargs)
    (Pdb) name
    'image'
    (Pdb) instance
    <plone.app.blob.field.BlobWrapper object at 0x10d4042a8>
    (Pdb) getAnnotation(instance)
    {'Archetypes.storage.AnnotationStorage-image': <plone.app.blob.field.BlobWrapper object at 0x10d430a28>}
    

    I really think you should not do this :-)

    If I got something wrong pls correct... it's kinda hard to understand this Archetypes behaviour.