restlib2 is an HTTP library which simplifies creating dynamic Web resources for Django. restlib2 focuses on embracing HTTP and enables clean transparent implementations of your resources.
This project is maintained by bruth
RESTlib2 is an HTTP library which simplifies creating dynamic Web resources in Python. RESTlib2 focuses on embracing HTTP and enables clean transparent implementations of your resources.
Web frameworks typically layer on the abstractions for handling the request/response process. These abstractions decrease implementation transparency and results in fragmented modifications of your response object.
Django, for example, is a full stack Web framework which has a middleware layer that enables processing and/or augmenting the request and response objects before and after the view (called a controller in MVC stacks) processes the request. Along with the middleware, there are numerous decorators which can be used to wrapped the view to modify the process the request headers or modify the response headers.
Django is not at fault here. It was not designed to provide a REST API with the notion of a resource. It's background and intent is to make it trivial to stand up websites and the core developers (and the community) take an extremely pragmatic approach to achieve this. That being said, for very simple APIs Django has a few helper functions for restricting which methods are allowed.
Aside from Django core, django-tastypie is the most popular app that provides an API for defining resources in Django. Unfortunately, tastypie has too complicated of an API and too many abstractions to understand how it works. The developer must work within the confines of this intricate and arbitrary API, rather than embracing and confidently working with the inferred API HTTP provides.
RESTlib2 intends to provide a more simple, natural and familiar API for creating dynamic Web resources in Python.
Barebone Example (no sugar)
import json
class Author(Resource):
supported_accept_types = ['application/json']
def get(self, request):
return json.dumps([{
'first_name': 'John',
'last_name': 'Doe',
'birth_date': '1954-03-29',
}, {
'first_name': 'Jane',
'last_name': 'Doh',
'birth_date': '1958-01-13',
}])
supported_accept_types
defines the supported encoding types and rejects
requests only does not accept application/json
get
method is only defined, post
, put
, patch
, and
delete
are not allowed for this resource. head
is accessible if
get
is defined and options
is always available unless the service
is set as unavailableWhen designing a REST API, an important decision to make up front is whether a chatty service is preferred, or a service that requires more bandwith per request.
The latter of the two is a result of embedding resources in other related
resources. For example, the first response entity below simply has a
reference to the endpoint where author id
is available. Thus in order to
get the data about the author, a second request is required.
The second response entity has the author data embedded in the book data. This will require more bandwith, but will not require subsequent requests.
// Referenced
{
"title": "Learn Python The Hard Way",
"publish_date": "2010-05-01",
"author": {
"id": 1,
"url": "http://example.com/author/1"
}
}
// Embedded
{
"id": 1,
"title": "Learn Python The Hard Way",
"publish_date": "2010-05-01",
"author": {
"id": 1,
"first_name": "Zed",
"last_name": "Shaw"
// ...
}
}
Deciding whether to embed data in another entity can be answered with a simple question:
Is it likely the two entities are represented together in client applications?
Note that embedding resources does not preclude resources from having their own endpoints (as in the first example).
For a large collection of objects, choosing to not embed related resources results in the client needing to potentially make hundreds or thousands additional requests.
It is recommended to use django-preserialize for easily creating custom nested objects. By default, preserialize will recursively embed any local fields and local related objects.
from preserialize.serialize import serialize
from library.models import Book
class Author(Resource):
supported_accept_types = ['application/json']
def get(self, request):
data = serialize(Book.objects.all())
return json.dumps(data)
For a bit more control, the fields to be included or excluded can be specified:
from preserialize.serialize import serialize
from library.models import Book
class Author(Resource):
supported_accept_types = ['application/json']
def get(self, request):
author_template = {
'fields': ['first_name', 'last_name']
}
book_template = {
'fields': ['id', 'title', 'publish_date', 'author']
# Define the serialization template for the related author
'related': {
'author': author_template,
}
}
data = serialize(Book.objects.all(), **book_template)
return json.dumps(data)