Skip to main content

A flexible REST framework for Django

Project description

Delicious Cake

Delicious Cake is a flexible, Tastypie derived, REST framework for

Over the years I've used both Tastypie and Piston to create RESTful
APIs. While they both have their advantages neither is as flexible as
plain old Django views.

Why Delicious Cake?

Delicious Cake is a framework that removes much of the pain of creating
RESTful APIs, without imposing many constraints.

How is this different from Tastypie?

Tastypie makes taking your models and quickly exposing them in a RESTful
manner extremely easy. Unfortunately, that's not often the best way to
expose an api. Some functionality is simply difficult to express within
Tastypie's constraints.

Tastypie resources tightly couple several features: List views, detail
views, data hydration/dehydration, url construction, and
pre-serialization processing of objects. For simple APIs this can reduce
the amount of code necessary to get your project off the ground. As your
project evolves it can become increasingly difficult to express your
ideal API with Tastypie.

Delicious Cake is an attempt to take the best of Tastypie and present it
in a more flexible form.

This is an experiment

I want to get this out quickly to see if there is any interest in this
model of API development.

Both tests and documentation are sparse. If there's enough interest,
test coverage and documentation will become a priority.

Let me know what you think!


Did you know that Delicious Cake is on PyPi?


Or run:

pip install delicious-cake

What does it look like?


class CakeListResource(ListResource):
'''A simple list view'''
def get(self, request, *args, **kwargs):
return Cake.objects.all()

def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)

def post(self, request, *args, **kwargs):
cake_form = CakeForm(request.DATA)

if not cake_form.is_valid():
raise ValidationError(cake_form.errors)

# Return the newly created instance and indicate that
# HTTP 201 CREATED should be used in the response.

# return object, created (boolean)
return, True

def delete(self, request, *args, **kwargs):

# Used to get the base uri when paginating.
def get_resource_uri(self):
return ('cake-list',)

class Meta(object):
# See delicious_cake/ for more 'Resource' options.

# 'Entity' classes are used to pre-process objects before
# serialization.

# The 'list_entity_cls' will be used to pre-process the returned
# objects when viewed as a list.
list_entity_cls = CakeListEntity

# The 'detail_entity_cls' will be used to pre-process the returned
# objects when returned individually.
detail_entity_cls = CakeDetailEntity

# If the same representation of the object is used in both list and
# details views the 'entity_cls' option can be used
# (e.g. entity_cls = CakeDetailEntity)

class CakeDetailResource(DetailResource):
'''A simple detail view'''
def get(self, request, *args, **kwargs):
return get_object_or_404(Cake, pk=kwargs['pk'])

def put(self, request, *args, **kwargs):
pk = kwargs['pk']

created = False
instance = Cake.objects.get(pk=pk)
except Cake.DoesNotExist:
created = True
instance = Cake(id=pk)

cake_form = CakeForm(request.DATA, instance=instance)

if not cake_form.is_valid():
raise ValidationError(cake_form.errors)

# Return the newly created instance and indicate that
# HTTP 201 CREATED should be used in the response.
# OR
# Return the updated instance with HTTP 200 OK
return, created

def delete(self, request, *args, **kwargs):
get_object_or_404(Cake, pk=kwargs['pk']).delete()

def head(self, request, *args, **kwargs):
return self.get(self, request, *args, **kwargs)

class Meta(object):
detail_entity_cls = CakeDetailEntity

class CakeListResourceExtra(ListResource):
# Add a response header to all responses.
def process_http_response(self, http_response, entities):
http_response['X-The-Cake-Is-A-Lie'] = False

# Add a response header to all GET responses.
def process_http_response_get(self, http_response, entities):
http_response['X-Cake-Count'] = len(entities)

def get(self, request, *args, **kwargs):
# Tell the resource to use the 'CakeDetailEntity' instead of the
# default ('CakeListEntity' in this case) by specifying 'entity_cls'.
return ResourceResponse(
Cake.objects.all(), entity_cls=CakeDetailEntity)

def post(self, request, *args, **kwargs):
cake_form = CakeForm(request.DATA)

if not cake_form.is_valid():
raise ValidationError(cake_form.errors)

cake =

# You can return 'ResourceResponse's if you need to
# use a custom 'HttpResponse' class or pass in specific parameters to
# the 'HttpResponse' class's constructor.

# For example, in this method we want to return an HTTP 201 (CREATED)
# response, with the newly created cake's uri in 'Location' header.
# To do this we set the 'response_cls' argument to 'http.HttpCreated'
# and add a 'location' key to 'response_kwargs' dict.

# This is equilivant to returning ", created"

# In this case, the value passed into the location parameter of our
# 'HttpCreated' response will be a callable. When invoked it will be
# passed one parameter, the entity created from our cake object.

# And, just for fun, let's set 'include_entity' to False.

# So again, we'll return HTTP 201 (CREATED), with a Location header,
# the X-The-Cake-Is-A-Lie header, and no entity body.

return ResourceResponse(
cake, include_entity=False,
'location': lambda entity: entity.get_resource_uri()})

def get_resource_uri(self):
return ('cake-list-extra',)

class Meta(object):
entity_cls = CakeListEntity

urlpatterns = patterns('',
url(r'^cake/(?P<pk>\d+)/$', CakeDetailResource.as_view(), name='cake-detail'),
url(r'^cake/$', CakeListResource.as_view(), name='cake-list'),
url(r'^cake/extra/$', CakeListResourceExtra.as_view(), name='cake-list-extra'),)

class CakeEntity(Entity):
def get_resource_uri(self):
return ('cake-detail', (,))

class CakeListEntity(CakeEntity):

resource_id = fields.IntegerField(attr='pk')
cake_type = fields.CharField(attr='cake_type')

def process_cake_type(self, cake_type):
return self.CAKE_TYPE_CHOICES_LOOKUP.get(cake_type, 'Unknown')

class CakeDetailEntity(CakeListEntity):
message = fields.CharField()

class Cake(models.Model):

(CAKE_TYPE_BIRTHDAY, u'Birthday Cake',),
(CAKE_TYPE_GRADUATION, u'Graduation Cake',),
(CAKE_TYPE_SCHADENFREUDE, u'Shameful Pride Cake',),)

message = models.CharField(max_length=128)
cake_type = models.PositiveSmallIntegerField(db_index=True)

Project details

Release history Release notifications

This version
History Node


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
delicious-cake-0.0.10.tar.gz (33.3 kB) Copy SHA256 hash SHA256 Source None Jun 1, 2013

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging CloudAMQP CloudAMQP RabbitMQ AWS AWS Cloud computing Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page