State of REST

By: Curtis Maloney

FunkyBob on #django

Introduction

(aka: Who is this guy?)

  • Started using Python 1.5
  • Started using Django 0.91

  • django-nap
  • django-rated

  • Kogan (IE7 Tax)

The contenders

TastyPie (v0.9.16) [Since Mar 2010](2,111)
  • Incumbent
  • Has _everything_
  • Monolithic "Resource" design
Django REST Framework (v2.3) [Since Dec 2010](1,297)
  • v2.0 recently major redesign
  • CBV + Serialiser
django-nap (v0.12.2) [Since Dec 2012](65)
  • Small, simple, fast
  • Publisher + Serialiser

Django API Frameworks:  Features

  • Authorisation/Authentication
  • Data formats
  • Serialiser
  • Shapes
  • Pagination
  • Rate limiting
  • Filtering
  • API Versioning
  • Generic Views
  • Use in Views

Authentication

Who are you?

Authentication: 

TastyPie

  • HTTP Basic
  • HTTP Digest
  • API Key
  • Session/Django
  • OAuth 1.0a
  • Multi / Custom

Authentication:

DRF

  • HTTP Basic
  • HTTP Digest *
  • API Key
  • Session/Django
  • OAuth 1.0a
  • OAuth 2
  • Custom

*requires 3rd party tool

Authentication:

Nap

None*

Authorisation:


May I?

Authorisation:

TastyPie

Per-Publisher "authorization" provider.
  • read_list
  • read_detail
  • create_list
  • create_detail
  • update_list
  • update_detail
  • delete_list
  • delete_detail

AuthoriSation:

DRF
  • Global, shared, per-view.

has_permission(request, view)
has_object_permission(request, view, obj)

Authorisation:

Nap

@permit(test_func)

Data Formats

Format TastyPie DRF Nap
JSON
JSONP
XML
YAML
HTML (forms)
PLIST
MsgPack

Serialiser

From object to data, and back again!

Framework Structure Stateful? Pattern
TastyPie Monolitic Stateless Build + Set
DRF Stand-alone Stateful restore_object
Nap Stand-alone Stateless restore_object

Shapes

TastyPie

Can define fields as list/detail only.

DRF

Per-view selection.

Nap

Per-view selection.

Pagination

TastyPie

Custom class.
Offset + Limit

DRF

Django native.
Page + Size

Nap

Django native.
Page + Size / Offset + Limit
*Page granular

Rate Limiting

TastyPie

Built-in

DRF

Built-in.
Global or Scoped
Anon or User

Nap

None.
(use django-rated)

Filtering

TastyPie

Django ORM
Whitelist

DRF

Pluggable
Whitelist

Nap

None*
Override Publisher
get_object_list

API Versioning

TastyPie

Resource wrapper


DRF

Router + ViewSet*


Nap

Publisher wrapper.

Generic Views

TastyPie

None


Nap

Not Yet

Generic Views

DRF

  • GenericAPIView


Mixins

  • ListModelMixin
  • CreateModelMixin
  • RetrieveModelMixin
  • UpdateModelMixin
  • DestroyModelMixin

Concrete View Classes

  • CreateAPIView
  • ListAPIView
  • RetrieveAPIView
  • DestroyAPIView
  • UpdateAPIView
  • ListCreateAPIView
  • RetrieveUpdateAPIView
  • RetrieveDestroyAPIView
  • RetrieveUpdateDestroyAPIView

Use in views

TastyPie: clumsy

Need to replicate a major internal function.
resource = ThingResource()
    
def thing_list(request, **kwargs):
    base_bundle = self.build_bundle(request=request)
    objects = resource.obj_get_list(bundle=base_bundle, **resource.remove_api_resource_names(kwargs))
    sorted_objects = resource.apply_sorting(objects, options=request.GET)
    
    paginator = resource._meta.paginator_class(request.GET,
        sorted_objects,
        resource_uri=resource.get_resource_uri(),
        limit=resource._meta.limit,
        max_limit=resource._meta.max_limit,
        collection_name=resource._meta.collection_name
    )
    to_be_serialized = paginator.page()

    bundles = [
        resource.full_dehydrate(
            resource.build_bundle(obj=obj, request=request),
            for_list=True
        )
        for obj in to_be_serialized[resource._meta.collection_name]
    ]

    to_be_serialized[resource._meta.collection_name] = bundles
    to_be_serialized = resource.alter_list_data_to_serialize(request, to_be_serialized)

    return render(request, 'thing/thing_list.html', {'resource': to_be_serialized})
    

Use in views

DRF

Core design!

Use in views

Nap: Simpler
def thing_list(request, **kwargs):
    try:
        publisher = ThingPublisher(request, **kwargs)
        data = publisher.get_page(publisher.get_object_list())
        serialiser = publisher.get_serialiser()
        data['objects'] = serialiser.list_deflate(data['objects'])
    except http.BaseHttpResponse as e:
        return e

    return render(request, 'thing/thing_list.html', {'resource': data})

Questions?

Thanks for listening!  

HTH hAND!



github.com/funkybob/django-nap

State of REST

By Curtis Maloney

State of REST

  • 1,495