Getting started with Django tastypie

Why use REST

You have a database backed web application. This application tracks expenses. The application allows the capability to enter your expenses, view all your expenses, delete an expense etc. Essentially this application provides CRUD functionality. Django application has access to database credentials, but they are never seen by the users of the web application. Django application decides what to show to which user. Django application ensures that a particular user only sees the expenses entered by him and not somebody else’s expenses.

Now you want to provide a mobile application (Android or iOS) corresponding to this web application. Android application should allow the user to view his expenses, create an expense as well as any other CRUD functionality. But database credentials could not be put in Android code as it is not too hard to decompile an apk and get the db credentials. And we never want a user to get the db credentials else he will be in a position to see everyone’s expenses and the entire database. So there has to be another way to allow mobile applications to get things from the database. This is where REST comes into picture.

With REST, we have three components. A database, a Django application and a mobile application. Mobile application never accesses the database directly. It makes a REST api call to Django application. Mobile application also sends a api_key specific to the mobile user. Based on api_key, Django application determines what data to make visible to this particular api_key owner and sends the corresponding data in response.

Resource

REST stands for Representational State Transfer. It is a standard for transferring the state of a Resource, from web to mobile.

What do I mean by state of a Resource?

An expense could be a resource. A Person could be a resource. A blog post could be a resource. Basically any object or instance your program deals with could be a resource. And a resource’s state is maintained in it’s attributes. eg: You could have a model called Expense. The state of a expense instance is represented by its attributes.

Any REST library should be able to create and return a representation of such resource, which simply stated means that REST library should be able to tell us the attributes and their values for different model instances. And tastypie is adept at doing this.

Setting up the application

I am using Django 1.7. Some things might be different for you if you are using different version of Django.

You will find the representation of expense instances in objects key of response.

Get a particular expense

You can get the representation of expense with id 1 at

http://localhost:8000/api/expense/1/?format=json

See how you are able to hit these two urls without adding them in urlpatterns. These urlpatterns are added by tastypie internally.

How these endpoints help and ties with mobile application example?

If the mobile app wants to show all the expenses it could use the url http://localhost:8000/api/expense/?format=json, get the response, parse the response and show the result on app.

Right now every user will see all the expenses. As we move forward we will see how only a user’s expenses will be returned when a REST call is made from his/her mobile device.

Serialization

You must have realized that REST returns you serialized data. You might be wondering why use django-tastypie to achieve it, and not just use json.dumps. You can undoubtedly use json.dumps and not use django-tastypie to provide REST endpoints. But django-tastypie allows the ability to do many more things very easily as you will soon agree. Just hang on.

Changing Meta.resource_name

You can change ExpenseResource.Meta.resource_name from expense to expenditure.

With GET endpoints we were able to do the Read operations. With POST we will be able to do Create operations, as we will see in next section.

Handling POST

It’s hard to do POST from the browser. So we will use requests library to achieve this.

Check expense count before doing POST.

>>> Expense.objects.count()
2

Tastypie by default doesn’t authorize a person to do POST request. The default authorization class is ReadOnlyAuthorization which allows GET calls but doesn’t allow POST calls. So you will have to disallow authorization checks for the time being. Add the following to ExpenseResource.Meta

status_code 201 means that your Expense object was properly created. You can also verify it by checking that Expense count increased by 1.

>>> Expense.objects.count()
3

If you hit the GET endpoint from your browser, you will see this new Expense object too in the response. Try

http://localhost:8000/api/expense/?format=json

We have third commit at this point.

git checkout 749cf3

Explanation of POST

You need to POST at the same url where you get all the expenses. Compare the two urls.

One way of posting is to POST json encoded data. So we used json.dumps

If you are sending json encoded data, you need to send appopriate Content-type header too.

How this ties in with mobile

Android or iOS has a way to make POST request at a given url with headers. So you tell mobile app about the endpoint where they need to post and the data to post. They will call this rest endpoint, and the posted data will be handled by Django tastypie and proper row will be created in the database table.

Adding authentication

Currently GET and POST endpoints respond to every request. So even users who aren’t registered with the site will be able to see the expenses. Our first step is ensuring that only registered users are able to use the GET endpoints.

Api tokens and sessions

In a web application, a user logs in once and then she is able to make any number of web requests without being asked to login every time. eg: User logs in once and then can see her expense list page. After first request she can refresh the page, and can still get response without being asked for her login credentials again. This works because Django uses sessions and cookies to store user state. So browser sends a cookie to Django everytime the user makes a request, and Django app can associate the cookie with a user and shows the data for this particular user.

With mobile apps, there is no concept of sessions, unless the mobile is working with a WebView. The session corresponding thing in a mobile app is Api key. So an api key is associated with a user. Every REST call should include this api key, and then tastypie can use this key to verify whether a logged in user is making the request.

Creating user and api token

Let’s create an user in our system and a corresponding api token for her.

You need to ensure tastypie is in INSTALLED_APPS and you have migrated before you could create ApiKey instance.

The default authentication class provided by tastypie is Authentication which allows anyone to make GET requests. We need to set ExpenseResource.Meta.authentication to ensure that only users who provide valid api key are able to get response from GET endpoints.

Add the following on ExpensesResource.Meta.

authentication = ApiKeyAuthentication()

You need to import ApiKeyAuthentication.

from tastypie.authentication import ApiKeyAuthentication

Try the GET endpoint to get the list of expenses

http://localhost:8000/api/expense/?format=json

You will not see anything in response. If you see your runserver terminal, you’ll notice that status code 401 is raised.

With these we ensure that only registered users of the system with proper api key will be able to make GET requests.

Fourth commit at this point

git checkout 48725f

How this ties in with mobile app

When user installs the app, he logs in using his username and password for first time. These credentials are sent to Django server using a REST call. Django server returns the api key corresponding to this user to the mobile app. Mobile app stores this api token on mobile end and then uses this token for every subsequent REST call. User doesn’t have to provide the credentials anymore.

Since we already have some Expenses in db which aren’t associated with a User, so we kept User as a nullable field.

Make and run migrations

python manage.py makemigrations
python manage.py migrate

Right now our authorization class is set to Authorization. With this every user is authorized to see every expense. We will have to add a custom authorization class to enforce that users see only their expenses.

How mobile app will use it.

When Sheryl installs the app, she will be asked to login for the first time. There will be a REST endpoint which takes the username and password for a user and if the credentials are right, returns the api key for the user. Sheryl’s api key will be returned to the mobile app which will store it in local storage. And when Sheryl wants to see her expenses, this REST call will be made to Django server.

POST and create Sheryl’s expense

Till now if a POST request is made, even if with Sheryl’s api key, expense is created in db but is not associated with Sheryl.

We want to add functionality where if POST request is made from Sheryl’s device then expense is associated with Sheryl. If POST request is made from Mark’s device then expense should be associated with Mark.

Tastypie provides several hookpoints. We will use one such hookpoint. ModelResource provides a method called hydrate which we need to override. Add the following method to ExpenseResource.

Verify that Mark is still able to do GET

Since Mark doesn’t have any expense in db, so no object is there is objects key of response. Try creating an expense for this user from shell and then try the GET endpoint again.

Explicitly defining fields and customising them

ModelResource.Meta.fields can be dealt with in a similar way to ModelForm.Meta.fields. If you add the field to ModelResource.Meta.fields then it gets sane default behavriour. But it can be customised by adding the field explicitly on the ModelResource.