Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/smartpr/piston-perfect

Django Piston's API framework on steroids
https://github.com/smartpr/piston-perfect

Last synced: 3 months ago
JSON representation

Django Piston's API framework on steroids

Awesome Lists containing this project

README

        

piston-perfect
**************

Django Piston on steroids. Design inspired by slide 25 of
http://www.slideshare.net/landlessness/teach-a-dog-to-rest and the
reality of developing a RESTful web API for Smart.pr. Lives in a
module named ``piston_perfect`` that functions as a layer on top of
``piston``.

The contents of this module are modeled after the contents of
``piston`` (save for some subtle changes in naming).

You can use the following settings attribute to specify the data
formats that you want your API to support:

PISTON_FORMATS = 'json', # Defaults to *'json',*.

TODO:
Examples

``handlers``
============

class class piston_perfect.handlers.BaseHandler

All handlers should (directly or indirectly) inherit from this one.
Its public attributes and methods were designed with extensibility
in mind, so don't hesitate to override.

Note: Piston's ``piston.handler.BaseHandler.allowed_methods`` attribute
should not be used, as it is auto-generated based on the values
of ``create()``, ``read()``, ``update()`` and ``delete()``.

fields

Specifies the fields that are allowed to be included in a
response. It also serves as the set of options for request-level
fields selection (see ``request_fields``) and the base set of
fields that are allowed as incoming data (see
``may_input_field()``). If it is an empty iterable (the default)
the decision whether or not a field is allowed to be included is
taken by ``is_field_allowed()``.

Note that the value of this attribute is not automatically used
as field definition on the emitter, as is the behavior of
Piston's default base handler. See the monkey-patched
``piston.emitters.Emitter.construct()`` in ``patches`` for more
information.

request_fields

Determines if request-level fields selection is enabled. Should
be the name of the query string parameter in which the selection
can be found. If this attribute is defined as ``True`` (which is
the default) the default parameter name ``field`` will be used.
Note that setting to (as opposed to "defining as") ``True`` will
not work. Disable request-level fields selection by defining
this as ``False``.

Multiple fields can be specified by including the parameter
multiple times: ``?field=id&field=name`` is interpreted as the
selection ``('id', 'name')``.

exclude

Is used by ``is_field_allowed()`` to decide if a field should be
included in the result. Note that this only applies to scenarios
in which ``fields`` is empty. Should be an iterable of field
names and/or regular expression patterns. Its default setting is
to exclude all field names that begin with ``_``.

exclude_in

A list of field names that will be filtered out of incoming
data. Fields that are not listed in ``fields`` will never be
considered either, so this attribute should contain field names
that are also in ``fields``. Is used by ``may_input_field()``.

get_requested_fields(request)

Returns the fields selection for this specific request. Takes
into account the settings for ``fields`` and ``request_fields``,
and the query string in *request*. Returns ``()`` in case no
selection has been specified in any way.

may_output_field(field)

Determines if the field named *field* should be included in the
response. Returns ``False`` for any field that matches the
specification in ``exclude``, ``True`` otherwise. Note that this
method will not be consulted if ``fields`` is non-empty.

may_input_field(field)

Decides if a field should be filtered out of incoming data (in
the request body). The default behavior is to accept any fields
that are in ``fields`` (if not empty) and not in ``exclude_in``.

model_fields

The set of fields that is used to represent a model instance in
case no explicit fields set has been specified (either via
``fields`` or via a fields definition in the request).

classmethod model_key(instance)

Returns a key identifying the provided model instance.

classmethod model_type(instance)

Returns a text string representing the type of the provided
model instance.

classmethod model_description(instance)

Returns a description of the provided model instance.

authentication

The Piston authenticator that should be in effect on this
handler. If defined as ``True`` (which is not the same as
assigning ``True``, as this will not work) an instance of
``authentication.DjangoAuthentication`` is used. A value of
``None`` implies no authentication, which is the default.

validate(request, *args, **kwargs)

Validates and cleanses incoming data (in the request body). Can
be overridden to extend this behavior with other types of
request validation.

working_set(request, *args, **kwargs)

Returns the operation's base data set. No data beyond this set
will be accessed or modified. The reason why we need this one in
addition to ``data_set()`` is that ``data_item()`` needs to have
a data set to pick from -- we need to define which items it is
allowed to obtain (and which not). This data set should not have
user filters applied because those do not apply to item views.

data_set(request, *args, **kwargs)

Returns the operation's result data set, which is always an
iterable. The difference with ``working_set()`` is that it
returns the data *after* all filters and ordering (not slicing)
are applied.

data_item(request, *args, **kwargs)

Returns the data item that is being worked on. This is how the
handler decides if the requested data is singular or not. By
returning ``None`` we signal that this request should be handled
as a request for a set of data, as opposed to a request for a
single record.

data(request, *args, **kwargs)

Returns the data that is the result of the current operation,
without having to specify if the request is singular or plural.

get_response_data(request, response)

Reads the data from a response structure. Raises a *KeyError* if
response contains no data.

set_response_data(request, data, response=None)

Sets data onto a response structure. Creates a new response
structure if none is provided.

filters

User filter data query string parameter, or ``True`` if the
default (``filter``) should be used. Disabled (``False``) by
default.

filter_data(data, definition, values)

Applies user filters (as specified in ``filters``) to the
provided data. Does nothing unless overridden with a method that
implements filter logic.

order

Order data query string parameter, or ``True`` if the default
(``order``) should be used. Disabled (``False``) by default.

order_data(data, *order)

Orders the provided data. Does nothing unless overridden with a
method that implements ordering logic.

slice

Slice data query string parameter, or ``True`` if the default
(``slice``) should be used. Disabled (``False``) by default.

response_slice_data(response, request, *args, **kwargs)

Slices the data set in *response*. This method's job is to
interpret the order parameters in the request (if any),
translate them to a call to ``slice_data()`` and alter the
response respectively. Returns a boolean value that indicates
whether the data has been sliced or not.

slice_data(data, start=None, stop=None, step=None)

Slices the provided data according to *start*, *stop* and
*step*.

request(request, *args, **kwargs)

All requests are entering the handler here.

create(request, *args, **kwargs)

Default implementation of a create operation, put in place when
the handler defines ``create = True``.

read(request, *args, **kwargs)

Default implementation of a read operation, put in place when
the handler defines ``read = True``.

update(request, *args, **kwargs)

Default implementation of an update operation, put in place when
the the handler defines ``update = True``.

delete(request, *args, **kwargs)

Default implementation of a delete operation, put in place when
the the handler defines ``delete = True``.

response_add_debug(response, request)

Adds debug information to the response -- currently the database
queries that were performed in this operation. May be overridden
to extend with custom debug information.

response_constructed(response, unconstructed, request)

Is called right after the response has been constructed
(converted to a data structure with just dictionaries and
lists), and right before the response is being sent back to the
client. Allows for some last-minute operations that need the
guarantee of being the last, or that would impact the response
data if it hadn't been constructed yet.

data_safe_for_delete(data)

If we want the delete operation to remove data without impacting
the data in the response we can do it safely here.

class class piston_perfect.handlers.ModelHandler

Provides off-the-shelf CRUD operations on data of a certain model
type.

Note that in order to prevent accidental exposure of data that was
never intended to be public, model data fields will not be included
in the response if they are not explicitly mentioned in ``fields``.
If it is empty model data will be represented in a generic way as
specified by ``model_fields``.

model

A model class of type ``django.db.models.Model``.

exclude_nested

A list of field names that should be excluded from the fields
selection in case of a nested representation; i.e. when the
model is contained by another model object.

may_input_field(field)

validate(request, *args, **kwargs)

Turns the data on the request into model instances; a new
instance with the ``POST``'ed data or a current instance updated
with the ``PUT``'ed data.

working_set(request, *args, **kwargs)

data_item(request, *args, **kwargs)

filter_data(data, definition, values)

Recognizes and applies two types of filters:

* If its *definition* (the value of the filter in ``filters``)
is a text string, it will be interpreted as a filter on the
*QuerySet*.

* If its definition is a list (or tuple or set), it will be
interpreted as a search operation on all fields that are
mentioned in this list.

order_data(data, *order)

response_slice_data(response, request, *args, **kwargs)

create(request, *args, **kwargs)

update(request, *args, **kwargs)

data_safe_for_delete(data)

``authentication``
==================

class class piston_perfect.authentication.DjangoAuthentication

Piston authenticator that blocks all requests with non-
authenticated sessions.

``resource``
============

class class piston_perfect.resource.Resource(handler, authentication=None)

Simple subclass of Piston's implementation.

callmap

Route every request type to ``handlers.BaseHandler.request()``.

error_handler(e, *args, **kwargs)

If anything went wrong inside the handler, this method will try
to construct a meaningful error response (or not if we want to
hide the character of the problem from the user).

``patches``
===========

We need a few real hacks into Piston's internal logic, all of which
originate here. "Real hacks" as in; extensions that are kind of like
black magic because they require detailed knowledge of Piston's
workings at code level. They depend on some very specific pieces of
Piston code and are therefore likely to break with future versions of
Piston.

The idea is to have all the messy stuff collected in this one module
and isolate it as much as possible from the public-facing interfaces
in ``handlers``. Hopefully this will allow us to deal with Piston
updates without touching anything beyond this module.