Externalization

Standard Fields

Certain fields are generally useful for almost all objects. The names of these fields along with descriptions of their expected contents are contained in the namespace StandardExternalFields.

The function to_standard_external_dictionary is used to find and populate many of these fields. It is called automatically by the datastructures such as InterfaceObjectIO

Decorating

Many times, we have additional information we want to include with an external object that is somehow derived or not explicitly represented in the interfaces implemented by an object. For example, in a web application, we may want to provide an href value giving the URL at which a particular object may be found. This URL is derived from the currently executing request object in addition to the object being externalized.

For this purpose, we use decorators. Decorators are subscription adapters (or subscribers), meaning that there can be many of them registered for any given object, that implement IExternalObjectDecorator. They can be registered just for the object, or for the object and the request. Each time an object is externalized by to_external_object, the registered decorators will be invoked before the external object is returned.

Let’s continue with the example address we used before.

>>> import nti.externalization.tests.benchmarks
>>> from zope.configuration import xmlconfig
>>> _ = xmlconfig.file('configure.zcml', nti.externalization.tests.benchmarks)
>>> from nti.externalization.tests.benchmarks.objects import Address
>>> from nti.externalization.tests.benchmarks.objects import UserProfile
>>> home_address = Address(
...     full_name=u'Steve Jobs',
...     street_address_1=u'1313 Mockingbird Lane',
...     city=u'Salem',
...     state=u'MA',
...     postal_code=u'6666',
...     country=u'USA',
... )

This time we’ll create and register a decorator. Let’s pretend that we work in a sensitive system and we need to redact the addresses of users to meet security concerns. Notice that decorators are usually stateless, so it is faster to make them inherit from Singleton.

from zope.interface import implementer
from zope.component import adapter

from nti.externalization.interfaces import IExternalObjectDecorator
from nti.externalization.tests.benchmarks.interfaces import IAddress
from nti.externalization.singleton import Singleton

@implementer(IExternalObjectDecorator)
@adapter(IAddress)
class PrivateAddressDecorator(Singleton):

    def decorateExternalObject(self, address, external):
        for key in 'street_address_1', 'street_address_2', 'state', 'city', 'postal_code':
            del external[key]

We’ll register our adapter and externalize:

>>> from nti.externalization import to_external_object
>>> from zope import component
>>> component.provideSubscriptionAdapter(PrivateAddressDecorator)
>>> from pprint import pprint
>>> pprint(to_external_object(home_address))
{'Class': 'Address',
 'MimeType': 'application/vnd.nextthought.benchmarks.address',
 'country': 'USA',
 'full_name': 'Steve Jobs'}

If we provide a request, adapters for the (object, request) are also found:

class Request(object):
   url = 'http://example.com/path/'

@implementer(IExternalObjectDecorator)
@adapter(IAddress, Request)
class LinkAddressDecorator(object):

    def __init__(self, context, request):
        self.request = request

    def decorateExternalObject(self, address, external):
        external['href'] = self.request.url + 'address'

We can now provide a request when we externalize (if no request argument is given, the hook function get_current_request is used to look for a request):

>>> component.provideSubscriptionAdapter(LinkAddressDecorator)
>>> pprint(to_external_object(home_address, request=Request()))
{'Class': 'Address',
 'MimeType': 'application/vnd.nextthought.benchmarks.address',
 'country': 'USA',
 'full_name': 'Steve Jobs',
 'href': 'http://example.com/path/address'}

IExternalStandardDictionaryDecorator

There is also IExternalStandardDictionaryDecorator. It is called by to_standard_external_dictionary. Typically that’s well before most of the object-specific fields have been filled in (e.g., from the object schema), and it is always before IExternalObjectDecorator is used. There may be occasional uses for this, but it’s best to stick to IExternalObjectDecorator.

Dublin Core Metadata

Decorators for zope.dublincore metadata are installed for all objects by default. See nti.externalization.dublincore for more information.