Typically when you build a Django site and you need to expose your application's data in the form of a RESTful API you would use something like the excellent Django REST Framework. However if you are hosting your site on Google App Engine there is an alternative called Google Cloud Endpoints that you should check out.
Cloud Endpoints is a robust solution built on Google architecture that powers various Google APIs. It takes care of a lot of the hard work for you such as authentication via OAuth 2 and JSON templating and provides an explorer you can use to test your API in the browser.
An Endpoints API is a remote procedure call (RPC) service that provides remote methods accessible to external clients. Each Endpoints API consists of an RPC service class that subclasses the ProtoRPC remote.Service class, and one or more methods. When you define a method, you must also define message classes for the requests coming into that method and the responses returned by it. A message class performs a mapping function so the incoming data can be extracted and supplied to the service method properly, or supplied properly to the outgoing response - (http://goo.gl/PrLm89)
Suppose you have your site up and running on App Engine, the project has two
Django models called
PublicationYear and you
need to create an API endpoint to list all books published in a particular year.
Here is how you could go about it using Cloud Endpoints.
To get started create a new directory where your Django apps reside and call it
api. In here create three files:
books_api.py. Finally create the handler for the API in your
and ensure the
DJANGO_SETTINGS_MODULE environment variable is set.
- url: /_ah/spi/.* script: api.books_api.application env_variables: DJANGO_SETTINGS_MODULE: 'settings'
messages.py create the ProtoRPC message classes. In this example a
class should be created (if listing of publication years was also required, a
PublicationYear class would also be needed). Rule of thumb is one Django model
= one message class.
from protorpc import messages class Book(messages.Message): """Book ProtoRPC message Book fields needed to define the schema for methods. """ title = messages.StringField(1) author = messages.StringField(2) ebook_available = messages.BooleanField(3) publication_year = messages.IntegerField(4) #... class BookCollection(messages.Message): """Collection of Books.""" books = messages.MessageField(Book, 1, repeated=True) year = messages.IntegerField(2)
books_api.py create each endpoint. Below the book listing view is provided.
"""Books API implemented using Google Cloud Endpoints.""" import settings import endpoints from protorpc import messages from protorpc import message_types from protorpc import remote from books.models import Book from api import services from api.messages import BookCollection package = 'Api' @endpoints.api(name='books', version='v1', allowed_client_ids=[settings.GOOGLE_OAUTH2_CLIENT_ID, endpoints.API_EXPLORER_CLIENT_ID], scopes=[endpoints.EMAIL_SCOPE, ]) class BooksApi(remote.Service): """Books API v1.""" PUBLICATION_YEAR_RESOURCE = endpoints.ResourceContainer( message_types.VoidMessage, year=messages.IntegerField(1, variant=messages.Variant.INT32)) @endpoints.method(PUBLICATION_YEAR_RESOURCE, BookCollection, path='list', http_method='GET', name='books.list') def book_list(self, request): """List view endpoint.""" if hasattr(request, 'year'): book_list = Book.objects.filter( publication_year__year=request.year) else: book_list = Book.objects.all() books = services.ApiUtils.serialize_books(book_list) return BookCollection(books=books) application = endpoints.api_server([BooksApi], restricted=False)
protorpc packages are provided by the GAE Python SDK.
Authentication is required to access this endpoint, shown by the inclusion of
allowed_client_ids param. Make sure your OAuth settings are in your
settings.py file for this to work.
can be included as an option if you wish to use the API explorer at
The resource container
PUBLICATION_YEAR_RESOURCE passed to the endpoint allows
for the publication year to be provided as part of the request querystring, for
/_ah/api/books/v1/list?year=2005 will return all books published in
services.py create any additional utility classes required. In the code
ApiUtils.serialize_books() is referenced.
from django.forms.models import model_to_dict from api.messages import Book class ApiUtils(object): """Utility API functions.""" @staticmethod def serialize_books(books): """Function to serialize a queryset of Book models. Args: books: a queryset Returns: A list of Book ProtoRPC messages """ items =  for book in books: item = model_to_dict(book, fields=[ 'title', 'author', 'ebook_available']) item['publication_year'] = book.publication_year.year items.append(Book(**item)) return items
In ApiUtils you could also add some authorization functions if required, i.e.
does the authenticated user have an
@yourdomain.com email address or are they
in an approved list of users. Example:
user = endpoints.get_current_user() if not user.email().endswith('@yourdomain.com'): raise endpoints.UnauthorizedException('Unauthorized user.')
That's all that is required to get an API up and running using your existing Django models.
You could improve this solution by using the Django paginator
django.core.paginator.Paginator) rather than returning all the books in one
request. You would need to create a
PAGINATION_TOKEN_RESOURCE and pass this
into the endpoint so the user can provide a page number / token query param for