skip to navigation
skip to content

Not Logged In

delicious-cake 0.0.10

A flexible REST framework for Django

Delicious Cake
==============

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

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!

[mike@theitemshoppe.com](mailto:mike@theitemshoppe.com)

Did you know that Delicious Cake is on PyPi?
--------------------------------------------

[http://pypi.python.org/pypi/delicious-cake](http://pypi.python.org/pypi/delicious-cake)/

Or run:

    pip install delicious-cake

What does it look like?
-----------------------

**resources.py**:

```python
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 cake_form.save(), True

    def delete(self, request, *args, **kwargs):
        Cake.objects.all().delete()

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

    class Meta(object):
        # See delicious_cake/options.py 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']

        try:
            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 cake_form.save(), 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 = cake_form.save()

        # 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 "cake_form.save(), 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,
            response_cls=http.HttpCreated,
            response_kwargs={
                'location': lambda entity: entity.get_resource_uri()})

    @models.permalink
    def get_resource_uri(self):
        return ('cake-list-extra',)

    class Meta(object):
        entity_cls = CakeListEntity
```
**urls.py**:

```python
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'),)
```
**entities.py**:

```python
class CakeEntity(Entity):
    @models.permalink
    def get_resource_uri(self):
        return ('cake-detail', (self.obj.pk,))


class CakeListEntity(CakeEntity):
    CAKE_TYPE_CHOICES_LOOKUP = dict(Cake.CAKE_TYPE_CHOICES)

    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()
```
**models.py**:

```python
class Cake(models.Model):
    CAKE_TYPE_BIRTHDAY = 1
    CAKE_TYPE_GRADUATION = 2
    CAKE_TYPE_SCHADENFREUDE = 3

    CAKE_TYPE_CHOICES = (
        (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)
```
 
File Type Py Version Uploaded on Size
delicious-cake-0.0.10.tar.gz (md5) Source 2013-06-01 32KB
  • Downloads (All Versions):
  • 77 downloads in the last day
  • 396 downloads in the last week
  • 994 downloads in the last month