Permalink
699 lines (502 sloc) 21.3 KB

Tastypie Cookbook

Creating a Full OAuth 2.0 API

It is common to use django to provision OAuth 2.0 tokens for users and then have Tasty Pie use these tokens to authenticate users to the API. Follow this tutorial and use this custom authentication class to enable OAuth 2.0 authentication with Tasty Pie.

Adding Custom Values

You might encounter cases where you wish to include additional data in a response which is not obtained from a field or method on your model. You can easily extend the :meth:`~tastypie.resources.Resource.dehydrate` method to provide additional values:

Per-Request Alterations To The Queryset

A common pattern is needing to limit a queryset by something that changes per-request, for instance the date/time. You can accomplish this by lightly modifying get_object_list:

Using Your Resource In Regular Views

In addition to using your resource classes to power the API, you can also use them to write other parts of your application, such as your views. For instance, if you wanted to encode user information in the page for some Javascript's use, you could do the following. In this case, user_json will not include a valid resource_uri:

To include a valid resource_uri, the resource must be associated with an tastypie.Api instance, as below:

Alternatively, to get a valid resource_uri you may pass in the api_name parameter directly to the Resource:

Example of getting a list of users:

Then in template you could convert JSON into JavaScript object:

<script>
    var json = "{{ list_json|escapejs }}";
    var users = JSON.parse(json);
</script>

Using Non-PK Data For Your URLs

By convention, ModelResources usually expose the detail endpoints utilizing the primary key of the Model they represent. However, this is not a strict requirement. Each URL can take other named URLconf parameters that can be used for the lookup.

For example, if you want to expose User resources by username, you can do something like the following:

The added URLconf matches before the standard URLconf included by default & matches on the username provided in the URL.

Another alternative approach is to override the dispatch method:

Nested Resources

You can also do "nested resources" (resources within another related resource) by lightly overriding the prepend_urls method & adding on a new method to handle the children:

Adding Search Functionality

Another common request is being able to integrate search functionality. This approach uses Haystack, though you could hook it up to any search technology. We leave the CRUD methods of the resource alone, choosing to add a new endpoint at /api/v1/notes/search/:

from django.conf.urls import url, include
from django.core.paginator import Paginator, InvalidPage
from django.http import Http404
from haystack.query import SearchQuerySet
from tastypie.resources import ModelResource
from tastypie.utils import trailing_slash
from notes.models import Note


class NoteResource(ModelResource):
    class Meta:
        queryset = Note.objects.all()
        resource_name = 'notes'

    def prepend_urls(self):
        return [
            url(r"^(?P<resource_name>%s)/search%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_search'), name="api_get_search"),
        ]

    def get_search(self, request, **kwargs):
        self.method_check(request, allowed=['get'])
        self.is_authenticated(request)
        self.throttle_check(request)

        # Do the query.
        sqs = SearchQuerySet().models(Note).load_all().auto_query(request.GET.get('q', ''))
        paginator = Paginator(sqs, 20)

        try:
            page = paginator.page(int(request.GET.get('page', 1)))
        except InvalidPage:
            raise Http404("Sorry, no results on that page.")

        objects = []

        for result in page.object_list:
            bundle = self.build_bundle(obj=result.object, request=request)
            bundle = self.full_dehydrate(bundle)
            objects.append(bundle)

        object_list = {
            'objects': objects,
        }

        self.log_throttled_access(request)
        return self.create_response(request, object_list)

Creating per-user resources

One might want to create an API which will require every user to authenticate and every user will be working only with objects associated with them. Let's see how to implement it for two basic operations: listing and creation of an object.

For listing we want to list only objects for which 'user' field matches 'request.user'. This could be done by applying a filter in the authorized_read_list method of your resource.

For creating we'd have to wrap obj_create method of ModelResource. Then the resulting code will look something like:

camelCase JSON Serialization

The convention in the world of Javascript has standardized on camelCase, where Tastypie uses underscore syntax, which can lead to "ugly" looking code in Javascript. You can create a custom serializer that emits values in camelCase instead:

Pretty-printed JSON Serialization

By default, Tastypie outputs JSON with no indentation or newlines (equivalent to calling :py:func:`json.dumps` with indent set to None). You can override this behavior in a custom serializer:

Determining format via URL

Sometimes it's required to allow selecting the response format by specifying it in the API URL, for example /api/v1/users.json instead of /api/v1/users/?format=json. The following snippet allows that kind of syntax additional to the default URL scheme:

Adding to the Django Admin

If you're using the django admin and ApiKeyAuthentication, you may want to see or edit ApiKeys next to users. To do this, you need to unregister the built-in UserAdmin, alter the inlines, and re-register it. This could go in any of your admin.py files. You may also want to register ApiAccess and ApiKey models on their own.:

Using SessionAuthentication

If your users are logged into the site & you want Javascript to be able to access the API (assuming jQuery), the first thing to do is setup SessionAuthentication:

Then you'd build a template like:

<html>
    <head>
        <title></title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                // We use ``.ajax`` here due to the overrides.
                $.ajax({
                    // Substitute in your API endpoint here.
                    url: '/api/v1/users/',
                    contentType: 'application/json',
                    // The ``X-CSRFToken`` evidently can't be set in the
                    // ``headers`` option, so force it here.
                    // This method requires jQuery 1.5+.
                    beforeSend: function(jqXHR, settings) {
                        // Pull the token out of the DOM.
                        jqXHR.setRequestHeader('X-CSRFToken', $('input[name=csrfmiddlewaretoken]').val());
                    },
                    success: function(data, textStatus, jqXHR) {
                        // Your processing of the data here.
                        console.log(data);
                    }
                });
            });
        </script>
    </head>
    <body>
        <!-- Include the CSRF token in the body of the HTML -->
        {% csrf_token %}
    </body>
</html>

There are other ways to make this function, with other libraries or other techniques for supplying the token (see https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax for an alternative). This is simply a starting point for getting things working.