Vocabularies and Sources#

Vocabularies are a set of allowed choices that back a particular field. They contain so-called terms which represent those allowed choices. Sources are similar, but are a more generic and dynamic concept.

Concepts#

Vocabularies contain a list of terms. These terms are usually tokenized, meaning that in addition to a term's value, it also has a token, which is a machine-friendly identifier for the term in 7-bit ASCII.

Note

Since the underlying value of a term might not necessarily be serializable (it could be an arbitrary Python object), plone.restapi only exposes and accepts tokens. It will transparently convert between tokens and values during serialization and deseralization. For this reason, the following endpoints only support tokenized vocabularies and sources, and they do not expose the terms' values.

Terms can also have a title, which is intended to be the user-facing label for the term. For vocabularies or sources whose terms are only tokenized but not titled, plone.restapi will fall back to using the token as the term title.

Sources are similar to vocabularies, but they tend to be more dynamic in nature, and are often used for larger sets of terms. They are also not registered with a global name like vocabularies, but are instead addressed via the field they are assigned to.

Query Sources are sources that are capable of being queried or searched. The source will then return only the subset of terms that match the query.

The use of such a source is usually a strong indication that no attempt should be made to enumerate the full set of terms. Instead, the source should only be queried, for example, by presenting the user with an autocomplete widget.

Both vocabularies and sources can be context-sensitive. This means that they take the context into account and their contents may therefore change depending on the context in which they are invoked.

This section can only provide a basic overview of vocabularies and related concepts. For a more in-depth explanation please refer to the Plone documentation.

Endpoints overview#

In plone.restapi these three concepts are exposed through three separate endpoints, described in more detail below:

  • @vocabularies/<vocab_name>

  • @sources/<field_name>

  • @querysources/<field_name>?query=<search_query>

While the @vocabularies and @sources endpoints allow enumeration of terms and optionally filter terms server-side, the @querysources endpoint only allows for searching the respective source.

List all vocabularies#

GET (context)/@vocabularies#

To retrieve a list of all the available vocabularies, send a GET request to the @vocabularies endpoint:

http

GET /plone/@vocabularies HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET http://nohost/plone/@vocabularies -H "Accept: application/json" --user admin:secret

httpie

http http://nohost/plone/@vocabularies Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/@vocabularies', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))

The response will include a list with the URL (@id) and the names (title) of all the available vocabularies in Plone:

HTTP/1.1 200 OK
Content-Type: application/json

[
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.AvailableContentLanguages",
        "title": "plone.app.vocabularies.AvailableContentLanguages"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.SupportedContentLanguages",
        "title": "plone.app.vocabularies.SupportedContentLanguages"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Roles",
        "title": "plone.app.vocabularies.Roles"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Permissions",
        "title": "plone.app.vocabularies.Permissions"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.AllowedContentTypes",
        "title": "plone.app.vocabularies.AllowedContentTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.AllowableContentTypes",
        "title": "plone.app.vocabularies.AllowableContentTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.PortalTypes",
        "title": "plone.app.vocabularies.PortalTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes",
        "title": "plone.app.vocabularies.ReallyUserFriendlyTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.UserFriendlyTypes",
        "title": "plone.app.vocabularies.UserFriendlyTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Skins",
        "title": "plone.app.vocabularies.Skins"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Workflows",
        "title": "plone.app.vocabularies.Workflows"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.WorkflowStates",
        "title": "plone.app.vocabularies.WorkflowStates"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.WorkflowTransitions",
        "title": "plone.app.vocabularies.WorkflowTransitions"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.AvailableEditors",
        "title": "plone.app.vocabularies.AvailableEditors"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Keywords",
        "title": "plone.app.vocabularies.Keywords"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.SyndicationFeedTypes",
        "title": "plone.app.vocabularies.SyndicationFeedTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.SyndicatableFeedItems",
        "title": "plone.app.vocabularies.SyndicatableFeedItems"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Users",
        "title": "plone.app.vocabularies.Users"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Groups",
        "title": "plone.app.vocabularies.Groups"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Principals",
        "title": "plone.app.vocabularies.Principals"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Catalog",
        "title": "plone.app.vocabularies.Catalog"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Actions",
        "title": "plone.app.vocabularies.Actions"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.PortalActionCategories",
        "title": "plone.app.vocabularies.PortalActionCategories"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Timezones",
        "title": "plone.app.vocabularies.Timezones"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.CommonTimezones",
        "title": "plone.app.vocabularies.CommonTimezones"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.AvailableTimezones",
        "title": "plone.app.vocabularies.AvailableTimezones"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Weekdays",
        "title": "plone.app.vocabularies.Weekdays"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.WeekdaysAbbr",
        "title": "plone.app.vocabularies.WeekdaysAbbr"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.WeekdaysShort",
        "title": "plone.app.vocabularies.WeekdaysShort"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.Month",
        "title": "plone.app.vocabularies.Month"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.MonthAbbr",
        "title": "plone.app.vocabularies.MonthAbbr"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ImagesScales",
        "title": "plone.app.vocabularies.ImagesScales"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.MetadataFields",
        "title": "plone.app.vocabularies.MetadataFields"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.content.ValidAddableTypes",
        "title": "plone.app.content.ValidAddableTypes"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.contentrules.events",
        "title": "plone.contentrules.events"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/Behaviors",
        "title": "Behaviors"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/Fields",
        "title": "Fields"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.schemaeditor.VocabulariesVocabulary",
        "title": "plone.schemaeditor.VocabulariesVocabulary"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.formwidget.relations.cmfcontentsearch",
        "title": "plone.formwidget.relations.cmfcontentsearch"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.event.SynchronizationStrategies",
        "title": "plone.app.event.SynchronizationStrategies"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.contenttypes.metadatafields",
        "title": "plone.app.contenttypes.metadatafields"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/Interfaces",
        "title": "Interfaces"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.discussion.vocabularies.CaptchaVocabulary",
        "title": "plone.app.discussion.vocabularies.CaptchaVocabulary"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.discussion.vocabularies.TextTransformVocabulary",
        "title": "plone.app.discussion.vocabularies.TextTransformVocabulary"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.users.user_registration_fields",
        "title": "plone.app.users.user_registration_fields"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.users.group_ids",
        "title": "plone.app.users.group_ids"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.multilingual.vocabularies.AllContentLanguageVocabulary",
        "title": "plone.app.multilingual.vocabularies.AllContentLanguageVocabulary"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.multilingual.vocabularies.AllAvailableLanguageVocabulary",
        "title": "plone.app.multilingual.vocabularies.AllAvailableLanguageVocabulary"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.app.multilingual.RootCatalog",
        "title": "plone.app.multilingual.RootCatalog"
    },
    {
        "@id": "http://localhost:55001/plone/@vocabularies/plone.restapi.testing.context_vocabulary",
        "title": "plone.restapi.testing.context_vocabulary"
    }
]

Get a vocabulary#

GET (context)/@vocabularies/(vocab_name)#

To enumerate the terms of a particular vocabulary, use the @vocabularies endpoint with the name of the vocabulary, for example /plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes. The endpoint can be used with the site root and content objects:

http

GET /plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes -H "Accept: application/json" --user admin:secret

httpie

http http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))

The server will respond with a list of terms. The title is purely for display purposes. The token is what should be sent to the server to address that term.

Note

Vocabulary terms will be batched if the size of the resultset exceeds the batch size. See Batching for more details on how to work with batched results.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes",
    "items": [
        {
            "title": "Collection",
            "token": "Collection"
        },
        {
            "title": "Comment",
            "token": "Discussion Item"
        },
        {
            "title": "DX Test Document",
            "token": "DXTestDocument"
        },
        {
            "title": "Event",
            "token": "Event"
        },
        {
            "title": "File",
            "token": "File"
        },
        {
            "title": "Folder",
            "token": "Folder"
        },
        {
            "title": "Image",
            "token": "Image"
        },
        {
            "title": "Link",
            "token": "Link"
        },
        {
            "title": "News Item",
            "token": "News Item"
        },
        {
            "title": "Page",
            "token": "Document"
        },
        {
            "title": "Test Document",
            "token": "ATTestDocument"
        },
        {
            "title": "Test Folder",
            "token": "ATTestFolder"
        }
    ],
    "items_total": 12
}

By default, the vocabularies are batched. However, you can pass the parameter b_size=-1 to force the endpoint to return all the terms, instead of a batched response.

Filter Vocabularies#

GET (context)/@vocabularies/(vocab_name)?title=(filter_query)#
GET (context)/@vocabularies/(vocab_name)?token=(filter_query)#
GET (context)/@vocabularies/(vocab_name)?tokens=(filter_term1)&tokens=(filter_term2)&...#

Vocabulary terms can be filtered using the title, token, or tokens (array) parameter.

Use the title parameter to filter vocabulary terms by title. For example, search for all terms that contain the string doc in the title:

http

GET /plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?title=doc HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?title=doc' -H "Accept: application/json" --user admin:secret

httpie

http 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?title=doc' Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?title=doc', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?title=doc",
    "items": [
        {
            "title": "DX Test Document",
            "token": "DXTestDocument"
        },
        {
            "title": "Test Document",
            "token": "ATTestDocument"
        }
    ],
    "items_total": 2
}

Use the token parameter to filter vocabulary terms by token. This is useful when you have the token, and you need to retrieve the title. For example, search for the term doc in the token:

http

GET /plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?token=Document HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?token=Document' -H "Accept: application/json" --user admin:secret

httpie

http 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?token=Document' Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?token=Document', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?token=Document",
    "items": [
        {
            "title": "Page",
            "token": "Document"
        }
    ],
    "items_total": 1
}

Note

You must not filter by title and token at the same time. The API returns a 400 response code if you do so.

Use the tokens parameter to filter vocabulary terms by a list of tokens:

http

GET /plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?tokens=Document&tokens=Event HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?tokens=Document&tokens=Event' -H "Accept: application/json" --user admin:secret

httpie

http 'http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?tokens=Document&tokens=Event' Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?tokens=Document&tokens=Event', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:55001/plone/@vocabularies/plone.app.vocabularies.ReallyUserFriendlyTypes?tokens=Document&tokens=Event",
    "items": [
        {
            "title": "Event",
            "token": "Event"
        },
        {
            "title": "Page",
            "token": "Document"
        }
    ],
    "items_total": 2
}

Get a source#

GET (context)/@sources/(field_name)#

To enumerate the terms of a field's source, use the @sources endpoint on a specific context, and pass the field name as a path parameter, for example, /plone/doc/@sources/some_field.

Because sources are inherently tied to a specific field, this endpoint can only be invoked on content objects. The source is addressed via the field name for which it is used, instead of a global name (which sources do not have).

Otherwise, the endpoint behaves the same as the @vocabularies endpoint.

Example:

http

GET /plone/doc/@sources/test_choice_with_source HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET http://nohost/plone/doc/@sources/test_choice_with_source -H "Accept: application/json" --user admin:secret

httpie

http http://nohost/plone/doc/@sources/test_choice_with_source Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/doc/@sources/test_choice_with_source', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))

The server will respond with a list of terms. The title is purely for display purposes. The token is what should be sent to the server to address that term:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:55001/plone/doc/@sources/test_choice_with_source",
    "items": [
        {
            "title": "Title 1",
            "token": "token1"
        },
        {
            "title": "Title 2",
            "token": "token2"
        },
        {
            "title": "Title 3",
            "token": "token3"
        }
    ],
    "items_total": 3
}

Note

Technically there can be sources that are not iterable: ones that only implement ISource, but not IIterableSource. These cannot be enumerated using the @sources endpoint. It will respond with a corresponding error.

Querying a query source#

GET (context)/@querysources/(field_name)?query=(search_query)#

Query sources—sources that implement IQuerySource—can be queried using this endpoint, by passing the search term in the query parameter. This search term will be passed to the query source's search() method. The source's results are returned.

Example:

http

GET /plone/doc/@querysources/test_choice_with_querysource?query=2 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0

curl

curl -i -X GET 'http://nohost/plone/doc/@querysources/test_choice_with_querysource?query=2' -H "Accept: application/json" --user admin:secret

httpie

http 'http://nohost/plone/doc/@querysources/test_choice_with_querysource?query=2' Accept:application/json -a admin:secret

python-requests

requests.get('http://nohost/plone/doc/@querysources/test_choice_with_querysource?query=2', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))

The server will respond with a list of terms. The title is purely for display purposes. The token is what should be sent to the server to address that term:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "@id": "http://localhost:55001/plone/doc/@querysources/test_choice_with_querysource?query=2", 
  "items": [
    {
      "title": "Title 2", 
      "token": "token2"
    }
  ], 
  "items_total": 1
}

Note

Technically, even though sources that implement IQuerySource are required to implement __iter__ as well when strictly following the interface interitance hierarchy, they usually are used in Plone in situations where their full contents should not or cannot be enumerated. For example, imagine a source of all users, backed by a large LDAP.

For this reason, plone.restapi takes the stance that the IQuerySource interface is a strong indication that this source should only be queried, and therefore does not support enumeration of terms via the @querysources endpoint.

If the source does actually implement IIterableSource in addition to IQuerySource, it can still be enumerated via the @sources endpoint.