Resource

Kinto-Core provides a basic component to build resource oriented APIs. In most cases, the main customization consists in defining the schema of the records for this resource.

Full example

import colander

from kinto.core import resource
from kinto.core import utils


class BookmarkSchema(resource.ResourceSchema):
    url = colander.SchemaNode(colander.String(), validator=colander.url)
    title = colander.SchemaNode(colander.String())
    favorite = colander.SchemaNode(colander.Boolean(), missing=False)
    device = colander.SchemaNode(colander.String(), missing='')

    class Options:
        readonly_fields = ('device',)


@resource.register()
class Bookmark(resource.Resource):
    schema = BookmarkSchema

    def process_object(self, new, old=None):
        new = super().process_object(new, old)
        if new['device'] != old['device']:
            new['device'] = self.request.headers.get('User-Agent')

        return new

See the ReadingList and Kinto projects source code for real use cases.

URLs

By default, a resource defines two URLs:

  • /{classname}s for the list of records
  • /{classname}s/{id} for single records

Since adding an s suffix for the plural form might not always be relevant, URLs can be specified during registration:

@resource.register(plural_path='/user/bookmarks',
                   object_path='/user/bookmarks/{{id}}')
class Bookmark(resource.Resource):
    schema = BookmarkSchema

Note

The same resource can be registered with different URLs.

Schema

Override the base schema to add extra fields using the Colander API.

class Movie(resource.ResourceSchema):
    director = colander.SchemaNode(colander.String())
    year = colander.SchemaNode(colander.Int(),
                               validator=colander.Range(min=1850))
    genre = colander.SchemaNode(colander.String(),
                                validator=colander.OneOf(['Sci-Fi', 'Comedy']))

See the resource schema options to define schema-less resources or specify rules like readonly fields.

HTTP methods and options

In order to specify which HTTP verbs (GET, PUT, PATCH, …) are allowed on the resource, as well as specific custom Pyramid (or cornice) view arguments, refer to the viewset section.

Events

When a record is created/deleted in a resource, an event is sent. See the dedicated section about notifications to plug events in your Pyramid/Kinto-Core application or plugin.

Model

Plug custom model

In order to customize the interaction of a HTTP resource with its storage, a custom model can be plugged-in:

from kinto.core import resource


class TrackedModel(resource.Model):
    def create_record(self, record, parent_id=None):
        record = super().create_record(record, parent_id)
        trackid = index.track(record)
        record['trackid'] = trackid
        return record


class Payment(resource.Resource):
    default_model = TrackedModel

Relationships

With the default model and storage backend, Kinto-Core does not support complex relations.

However, it is possible to plug a custom model class, that will take care of saving and retrieving records with relations.

Note

This part deserves more love, please come and discuss!

In Pyramid views

In Pyramid views, a request object is available and allows to use the storage configured in the application:

from kinto.core import resource

def view(request):
    registry = request.registry

    flowers = resource.Model(storage=registry.storage,
                             resource_name='app:flowers')

    flowers.create_object({'name': 'Jonquille', 'size': 30})
    flowers.create_object({'name': 'Amapola', 'size': 18})

    min_size = resource.Filter('size', 20, resource.COMPARISON.MIN)
    objects = flowers.get_objects(filters=[min_size])

    flowers.delete_object(records[0])

Outside views

Outside views, an application context has to be built from scratch.

As an example, let’s build a code that will copy a collection into another:

from kinto.core import resource, DEFAULT_SETTINGS
from pyramid import Configurator


config = Configurator(settings=DEFAULT_SETTINGS)
config.add_settings({
    'kinto.storage_backend': 'kinto.core.storage.postgresql'
    'kinto.storage_url': 'postgresql://user:[email protected]:5432/dbname'
})
kinto.core.initialize(config, '0.0.1')

local = resource.Model(storage=config.registry.storage,
                       parent_id='browsing',
                       resource_name='history')

remote = resource.Model(storage=config_remote.registry.storage,
                        parent_id='',
                        resource_name='history')

records, total = in remote.get_records():
for record in records:
    local.create_record(record)

Custom record ids

By default, records ids are UUID4.

A custom record ID generator can be set globally in Configuration, or at the resource level:

from kinto.core import resource
from kinto.core import utils
from kinto.core.storage import generators


class MsecId(generators.Generator):
    def __call__(self):
        return '%s' % utils.msec_time()


@resource.register()
class Mushroom(resource.Resource):
    def __init__(request):
        super().__init__(request)
        self.model.id_generator = MsecId()

Python API

Resource

Schema

Model

Generators