Tastypie is a Web service API framework for Django. It is used to create REST style APIs, which can be used from any application, and it comes with the serialisation and authentication standard that can be used while developing REST APIs. This article covers the development of a REST API using Tastypie.
Tastypie is a powerful package for developing the REST API. It provides a convenient and powerful abstraction for developing the REST-style interface, which is very easy to implement. It also provides serialisation for XML and JSON. In this article, we will use JSON to return data.
For a better understanding of what this article covers, readers should be familiar with the basics of Django and the REST concept. In this article, I am going to cover the basics of Tastypie, such as resources, filtering and REST methods (GET, POST, PATCH, DELETE).
We will develop basic REST APIs for book management with a SQLite database and, along the way, we will learn to implement the REST API using Tastypie.
I am using Ubuntu 16.04 LTS as my development machine.
Installation
First, let’s install the following packages.
1. Virtualenv:
sudo apt-get install virtualenv
2. We will set up our project in virtualenv.
3. Create virtualenv and install the necessary packages in it:
virtualenv env
4. Activate virtualenv:
source env/bin/activate
5. Now, install the Django Tastypie package using Pip, as follows:
pip install django django-tastypie
Project set-up
1. We will create a Django project called my_api, with the following command:
django-admin startproject my_api
2. Switch to the my_api directory and create an app called books:
./manege.py startapp books
3. After these steps, our working directory should look like what’s shown below:
├── books
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
├── my_api
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── settings.py
│ ├── settings.pyc
│ ├── urls.py
│ └── wsgi.py
Let’s start working on the code now.
Creating models
Let’s create two models for the books app, as follows, in books/models.py:
class Publication(models.Model): “””Model for Publication “”” name = models.CharField(max_length=20) isbn = models.CharField(max_length=30) website = models.URLField() def __unicode__(self): return self.name class Book(models.Model): “””Model for Book “”” name = models.CharField(max_length=30, help_text=”Name of the Book”) author = models.CharField(max_length=30) description = models.TextField() price = models.IntegerField() publication = models.ForeignKey(Publication) def __unicode__(self): return self.name
Configuration of Tastypie
We only need to add tastypie in INSTALLED_APPS in settings.py.
INSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘books’, ‘tastypie’, ]
Creating resources
For implementing REST APIs, ‘resources’ needs to be created, and to integrate Tastypie we need to create resource classes. We will create resource classes for each model. So, create the books/api.py file, and add resource classes within that file.
from tastypie.resources import ModelResource from books.models import Book, Publication class PublicationResource(ModelResource): “””Resource for Publication “”” class Meta: queryset = Publication.objects.all() resource_name = ‘publications’ class BookResource(ModelResource): “””Resource for Book “”” class Meta: queryset = Book.objects.all() resource_name = ‘books’
As BookResource and PublicationResource are sub-classes of ModelResource, it will take all non-relational fields of models and maps to its own ApiFields, much like Django ModelForms.
In the above class, the resource_name is optional. If not provided, it will automatically be generated from the class name, removing any instance of the ‘Resource’ and lower-casing the string. So ‘BookResource’ will become only ‘book’.
Hooking up the resources
Once we have the resource classes, we can hook the resources in urls.py so that it generates API end points for each resource. Let’s create urls.py in our books app, as follows:
from django.conf.urls import url, include from tastypie.api import Api from books.api import BookResource, PublicationResource api = Api(api_name='v1') api.register(PublicationResource()) api.register(BookResource()) urlpatterns = [ url(r'api/', include(api.urls)), ]
Include books/urls.py in my_api/urls.py as follows:
urlpatterns = [ url(r’^admin/’, admin.site.urls), url(r’^’, include(‘books.urls’)), ]
Once we are done with these steps, we are ready to play with Tastypie and fire up the REST API for books and publications.
Before running the server, let’s first run the migrations command, which will sync models in the DB.
-./manage.py makemigrations ./manage.py migrate ./manage.py runserver
Using REST API URLs
Open another terminal and run a Curl command to hit API URLs.
curl http://localhost:8/000/api/v1/books/?format=json
It will return an output as follows:
{“meta”: {“limit”: 20, “next”: null, “offset”: 0, “previous”: null, “total_count”: 0}, “objects”: []}
In this output, we have an empty ‘objects’ list because we don’t have any books yet. So let’s use the POST method to add book records.
Creating new resources
Before adding a book resource, let’s first add one publication resource, which will be attached to the book, as the Book model has the publication field as a foreign key to the Publication model.
curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “HarperCollins”, “isbn”: “123-456”, “website”:”https://harpercollins.co.in/”}’ -i http://localhost
The above commands result in the following output:
HTTP/1.0 401 Unauthorized Date: Sun, 09 Oct 2016 13:10:02 GMT Server: WSGIServer/0.1 Python/2.7.12 X-Frame-Options: SAMEORIGIN Content-Type: text/html; charset=utf-8
Oops! We are getting 401 status because, for reasons linked to safety, Tastypie ships with an authorisation class that is set to ReadOnlyAuthorization. This makes it safe to expose the API on the Web and to prevent anyone from doing POST/PUT/DELETE. Let’s enable this in our resources, as follows:
from tastypie.authorization import Authorization from tastypie.resources import ModelResource from books.models import Book, Publication class PublicationResource(ModelResource): “””Resource for Publication “”” class Meta: queryset = Publication.objects.all() resource_name = ‘publications’ authorization = Authorization() class BookResource(ModelResource): “””Resource for Book “”” class Meta: queryset = Book.objects.all() resource_name = ‘books’ authorization = Authorization()
Warning
Enabling authorisation is great for testing, but it should only be used during development, and the API should not be exposed to the Web with such resources. I have given a link for authorisation/authentication in the References section. Please spend some time in understanding those concepts and learn how to integrate them in Tastypie after reading this article.
So, let’s now add a publication resource using POST:
curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “HarperCollins”, “isbn”: “123-456”, “website”:”https://harpercollins.co.in/”}’ -i http://localhost:8000/api/v1/publications/
What follows is the output resulting from the above command:
HTTP/1.0 201 Created Date: Sun, 09 Oct 2016 14:45:18 GMT Server: WSGIServer/0.1 Python/2.7.10 Vary: Accept X-Frame-Options: SAMEORIGIN Content-Type: text/html; charset=utf-8 Location: /api/publications/1/
Cool…we got the 201 HTTP status code, which means that the publication information that we provided has been added to the DB. So let’s get publication resources:
curl http://localhost:8000/api/v1/publications/?format=json
The above command will return publication resources as follows:
{“meta”: {“limit”: 20, “next”: null, “offset”: 0, “previous”: null, “total_count”: 1}, “objects”: [{“id”: 1, “isbn”: “123-456”, “name”: “HarperCollins”, “resource_uri”: “/api/v1/publications/1/”, “website”: “https://harpercollins.co.in/”}]}
In this JSON, “meta” is the meta information about the records returned; “limit” is the number of records to return in one API call — we can configure the limit in settings.py; “next” and “previous” contain the URLs for fetching the next 20 records and previous 20 records, but as we have only one record, they are null; “total_count” is the total record available for the resources; and, finally, “objects” is the dictionary list, which contains a list of the recorded information.
Tastypie does not handle representation of foreign keys by default, so we need to instruct it how to represent the foreign key field by adding the ForeignKey field to Resource. Configure BookResource to include publication as the foreign key field.
from tastypie import fields class BookResource(ModelResource): “””Resource for Book “”” publication = fields.ForeignKey(PublicationResource, ‘publication’) class Meta: queryset = Book.objects.all() resource_name = ‘books’ authorization = Authorization()
The first parameter to fields.ForeignKey is the related field model resource class, and the second parameter is the related field name of the model (i.e., publication is the foreign key field of the Book model).
Now create the Book resource using POST, as follows:
curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “The Alchemist”, “author”: “Paulo Coelho”, “description”:”The Alchemist is a novel by Brazilian author Paulo Coelho which was first published in 1988.”, “price”: “350”, “publication”:”/api/v1/publications/1/”}’ -i http://localhost:8000/api/v1/books/
Notice that in the above command, the value for ‘publication’ key is the related resource detail’s URL; so book information is stored in the DB with publication_id as 1.
Let’s get the book resources, as follows:
url http://localhost:8000/api/v1/books/ {“meta”: {“limit”: 20, “next”: null, “offset”: 0, “previous”: null, “total_count”: 1}, “objects”: [{“author”: “Paulo Coelho”, “description”: “The Alchemist is a novel by Brazilian author Paulo Coelho which was first published in 1988.”, “id”: 1, “name”: “The Alchemist”, “price”: 350, “publication”: “/api/v1/publications/1/”, “resource_uri”: “/api/v1/books/1/”}]}
To get the specific book resource, use ‘resource_uri’.
curl http://localhost:8000/api/v1/books/1/ {"author”:“Paulo Coelho”, “description”: “The Alchemist is a novel by Brazilian author Paulo Coelho which was first published in 1988.”, “id”: 1, “name”: “The Alchemist”, “price”: 350, “publication”: “/api/v1/publications/1/”, “resource_uri”: “/api/v1/books/1/”}
Filtering
Let’s add some more book information and the names of the book publishers.
curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “George Newnes”, “isbn”: “0192123092”, “website”:”https://www.arthur-conan-doyle.com”}’ -i http://localhost:8000/api/v1/publications/ curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “The Memoirs of Sherlock Holmes”, “author”: “Arthur Conan Doyle”, “description”:”The great detective series”, “price”: ₹ 450, “publication”:”/api/v1/publications/2/”}’ -i http://localhost:8000/api/v1/books/ curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “Westland Press”, “isbn”: “123-678”, “website”:”www.westlandbooks.in”}’ -i http://localhost:8000/api/v1/publications/ curl -H “Content-Type: application/json” -X POST -d ‘{“name”: “The Immortals of Meluha”, “author”: “Amish Tripathi”, “description”:”The shiva trilogy 1”, “price”: 400, “publication”:”/api/v1/publications/3/”}’ -i http://localhost:8000/api/v1/books/
To use filters, add fields in resources to allow filters on those fields:
class PublicationResource(ModelResource): “””Resource for Publication “”” class Meta: queryset = Publication.objects.all() resource_name = ‘publications’ authorization = Authorization() filtering = { ‘name’: [‘exact’, ‘contains’] } class BookResource(ModelResource): “””Resource for Book “”” publication = fields.ForeignKey(PublicationResource, ‘publication’) class Meta: queryset = Book.objects.all() resource_name = ‘books’ authorization = Authorization() filtering = { ‘publication’: ALL_WITH_RELATIONS, ‘price’: [‘exact’, ‘lt’, ‘lte’, ‘gte’, ‘gt’], }
Now filter based on price
Curl http://localhost:8000/api/v1/books/?price= ₹ 450
This will return the books object with a price= ₹ 450.
{“meta”: {“limit”: 20, “next”: null, “offset”: 0, “previous”: null, “total_count”: 1}, “objects”: [{“author”: “Arthur Conan Doyle”, “description”: “The great detective series”, “id”: 2, “name”: “The Memoirs of Sherlock Holmes”, “price”: ₹ 450, “publication”: “/api/v1/publications/1/”, “resource_uri”: “/api/v1/books/2/”}]}
If we want all the books priced below ₹ 450, then use the “lt” keyword as follows:
curl http://localhost:8000/api/v1/books/?price__lt= ₹ 450 {“meta”: {“limit”: 20, “next”: null, “offset”: 0, “previous”: null, “total_count”: 2}, “objects”: [{“author”: “Paulo Coelho”, “description”: “The Alchemist is a novel by Brazilian author Paulo Coelho which was first published in 1988.”, “id”: 1, “name”: “The Alchemist”, “price”: 350, “publication”: “/api/v1/publications/1/”, “resource_uri”: “/api/v1/books/1/”}, {“author”: “Amish Tripathi”, “description”: “The shiva trilogy 1”, “id”: 3, “name”: “The Immortals of Meluha”, “price”: 400, “publication”: “/api/v1/publications/1/”, “resource_uri”: “/api/v1/books/3/”}]}
Filter based on the publication’s name is done the same way that we filter for the name field by adding it to resource filters.
Updating resources
To update the resource, we just need to provide fields to be updated for a specific resource.
curl -H “Content-Type: application/json” -X PATCH -d ‘{“name”:”The Nagas”}’ -i http://localhost:8000/api/v1/books/3/
Now get the resource using the GET method. Then the output should contain the updated books’ names.
Deleting resources
To delete the particular resource, use the DELETE method of REST.
curl -H “Content-Type: application/json” -X DELETE -i http://localhost:8000/api/v1/books/3/
Now, when you GET the book with ID 3, the 404 status should be returned.
curl -H -i http://localhost:8000/api/v1/books/3/
To summarise, Tastypie is a great framework for implementing the REST API in Django. A major advantage is the great community that stands behind it, resulting in good documentation and it being well tested. In this article, we looked at the basic CRUD operation on resources, and we can now implement custom authentication and authorisation, override the method of resource classes, and implement our own method as per our requirements.
Awesome, Great explanation.