Auto incrementing IDs for MongoDB

If you’re familiar with relational databases like MySQL or PostgreSQL, you’re probably also familiar with auto incrementing IDs. You select a primary key for a table and make it auto incrementing. Every row you insert afterwards, each of them gets a new ID, automatically incremented from the last one. We don’t have to keep track of what number comes next or ensure the atomic nature of this operation (what happens if two different client wants to insert a new row at the very same time? do they both get the same id?). This can be very useful where sequential, numeric IDs are essential. For example, let’s say we’re building a url shortener. We can base62 encode the ID of the url id to quickly generate a short slug for that long url.

Fast forward to MongoDB, the popular NoSQL database doesn’t have any equivalent to sequential IDs. It’s true that you can insert anything unique as the required _id¬†field of a mongodb document, so you can take things to your hand and try to insert unique ids yourselves. But you have to ensure the uniqueness and atomicity of the operation.

A very popular work around to this is to create a separate mongodb collection. Then maintain documents with a numeric value to keep track of your auto incrementing IDs. Now, every time we want to insert a new document that needs a unique ID, we come back to this collection, use the $inc operator to atomically increment this number and then use the incremented number as the unique id for our new document.

Let me give an example, say we have an messages collection. Each new message needs a new, sequential ID. We create a new collection named sequences. Each document in this sequences collection will hold the last used ID for a collection. So, for tracking the unique ID in the messages collection, we create a new document in the sequences collection like this:

{
    "_id" : "messages",
    "value" : 0
}

Next, we will write a function that can give us the next sequential ID for a collection by it’s name. The code is in Python, using PyMongo library.

def get_sequence(name):
    collection = db.sequences
    document = collection.find_one_and_update({"_id": name}, {"$inc": {"value": 1}}, return_document=True)

    return document["value"]

If we need the next auto incrementing ID for the messages collection, we can call it like this:

{"_id": get_sequence("messages")}
Find and Modify – Deprecated

If you have searched on Google, you might have come across many StackOverflow answers as well as individual blog posts which refer to findAndModify()¬†call (find_and_modify¬†in Pymongo). This was the way to do things. But it’s deprecated now, so please use the new find_one_and_update¬†function now.

(How) Does this scale?

We would only call the get_sequence function before inserting a new mongo document. The function uses the $inc operator which is atomic in nature. Mongo guarantees this. So even if 100s of different clients trying to increment the value for the same document, they will be all applied one after one. So each value they get will be unique, new IDs.

I personally haven’t been able to test this strategy at a larger scale but according to people on StackOverflow and other forums, people have scaled this to thousands and millions of users. So I guess it’s pretty safe.

API Star: Python 3 API Framework

For building quick APIs in Python, I have mostly depended on Flask. Recently I came across a new API framework for Python 3 named “API Star” which seemed really interesting to me for several reasons. Firstly the framework embraces modern Python features like type hints and asyncio. And then it goes ahead and uses these features to provide awesome development experience for us, the developers. We will get into those features soon but before we begin, I would like to thank Tom Christie for all the work he has put into Django REST Framework and now API Star.

Now back to API Star – I feel very productive in the framework. I can choose to write async codes based on asyncio or I can choose a traditional backend like WSGI. It comes with a command line tool –¬†apistar¬†to help us get things done faster. There’s (optional) support for both Django ORM and SQLAlchemy.¬† There’s a brilliant type system that enables us to define constraints on our input and output and from these, API Star can auto generate api schemas (and docs), provide validation and serialization feature and a lot more. Although API Star is heavily focused on building APIs, you can also build web applications on top of it fairly easily. All these might not make proper sense until we build something all by ourselves.

Getting Started

We will start by installing API Star. It would be a good idea to create a virtual environment for this exercise. If you don’t know how to create a virtualenv, don’t worry and go ahead.

pip install apistar

If you’re not using a virtual environment or the pip¬†command for your Python 3 is called pip3, then please use pip3 install apistar¬†instead.

Once we have the package installed, we should have access to the apistar¬†command line tool. We can create a new project with it. Let’s create a new project in our current directory.

apistar new .

Now we should have two files created – app.py¬†– which contains the main application and then test.py¬†for our tests. Let’s examine our app.py¬†file:

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls


def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}


routes = [
    Route('/', 'GET', welcome),
    Include('/docs', docs_urls),
    Include('/static', static_urls)
]

app = App(routes=routes)


if __name__ == '__main__':
    app.main()

Before we dive into the code, let’s run the app and see if it works. If we navigate to http://127.0.0.1:8080/¬†we will get this following response:

{"message": "Welcome to API Star!"}

And if we navigate to: http://127.0.0.1:8080/?name=masnun

{"message": "Welcome to API Star, masnun!"}

Similarly if we navigate to: http://127.0.0.1:8080/docs/, we will see auto generated docs for our API.

Now let’s look at the code. We have a welcome¬†function that takes a parameter named name¬†which has a default value of None. API Star is a smart api framework. It will try to find the name¬†key in the url path or query string and pass it to our function. It also generates the API docs based on it. Pretty nice, no?

We then create a list of Route and Include instances and pass the list to the App instance. Route objects are used to define custom user routing. Include , as the name suggests, includes/embeds other routes under the path provided to it.

Routing

Routing is simple. When constructing the App instance, we need to pass a list as the routes argument. This list should comprise of Route or Include objects as we just saw above. For Routes, we pass a url path, http method name and the request handler callable (function or otherwise). For the Include instances, we pass a url path and a list of Routes instance.

Path Parameters

We can put a name inside curly braces to declare a url path parameter. For example /user/{user_id}¬†defines a path where the user_id¬†is a path parameter or a¬† variable which will be injected into the handler function (actually callable). Here’s a quick example:

from apistar import Route
from apistar.frameworks.wsgi import WSGIApp as App


def user_profile(user_id: int):
    return {'message': 'Your profile id is: {}'.format(user_id)}


routes = [
    Route('/user/{user_id}', 'GET', user_profile),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

If we visit http://127.0.0.1:8080/user/23 we will get a response like this:

{"message": "Your profile id is: 23"}

But if we try to visit http://127.0.0.1:8080/user/some_string¬†– it will not match. Because the user_profile¬†function we defined, we added a type hint for the user_id¬†parameter. If it’s not integer, the path doesn’t match. But if we go ahead and delete the type hint and just use user_profile(user_id), it will match this url. This is again API Star is being smart and taking advantages of typing.

Including / Grouping Routes

Sometimes it might make sense to group certain urls together. Say we have a user module that deals with user related functionality. It might be better to group all the user related endpoints under the /user path. For example Р/user/new, /user/1, /user/1/update and what not. We can easily create our handlers and routes in a separate module or package even and then include them in our own routes.

Let’s create a new module named user, the file name would be user.py. Let’s put these codes in this file:

from apistar import Route


def user_new():
    return {"message": "Create a new user"}


def user_update(user_id: int):
    return {"message": "Update user #{}".format(user_id)}


def user_profile(user_id: int):
    return {"message": "User Profile for: {}".format(user_id)}


user_routes = [
    Route("/new", "GET", user_new),
    Route("/{user_id}/update", "GET", user_update),
    Route("/{user_id}/profile", "GET", user_profile),
]

Now we can import our user_routes from within our main app file and use it like this:

from apistar import Include
from apistar.frameworks.wsgi import WSGIApp as App

from user import user_routes

routes = [
    Include("/user", user_routes)
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

Now /user/new will delegate to user_new function.

Accessing Query String / Query Parameters

Any parameters passed in the query parameters can be injected directly into handler function. Say for the url /call?phone=1234, the handler function can define a phone¬†parameter and it will receive the value from the query string / query parameters. If the url query string doesn’t include a value for phone, it will get None¬†instead. We can also set a default value to the parameter like this:

def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}

In the above example, we set a default value to name which is None anyway.

Injecting Objects

By type hinting a request handler, we can have different objects injected into our views. Injecting request related objects can be helpful for accessing them directly from inside the handler. There are several built in objects in the http¬†package from API Star itself. We can also use it’s type system to create our own custom objects and have them injected into our functions. API Star also does data validation based on the constraints specified.

Let’s define our own User¬†type and have it injected in our request handler:

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar import typesystem


class User(typesystem.Object):
    properties = {
        'name': typesystem.string(max_length=100),
        'email': typesystem.string(max_length=100),
        'age': typesystem.integer(maximum=100, minimum=18)
    }

    required = ["name", "age", "email"]


def new_user(user: User):
    return user


routes = [
    Route('/', 'POST', new_user),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

Now if we send this request:

curl -X POST \
  http://127.0.0.1:8080/ \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{"name": "masnun", "email": "[email protected]", "age": 12}'

Guess what happens? We get an error saying age must be equal to or greater than 18. The type system is allowing us intelligent data validation as well. If we enable the docs url, we will also get these parameters automatically documented there.

Sending a Response

If you have noticed so far, we can just pass a dictionary and it will be JSON encoded and returned by default. However, we can set the status code and any additional headers by using the Response¬†class from apistar. Here’s a quick example:

from apistar import Route, Response
from apistar.frameworks.wsgi import WSGIApp as App


def hello():
    return Response(
        content="Hello".encode("utf-8"),
        status=200,
        headers={"X-API-Framework": "API Star"},
        content_type="text/plain"
    )


routes = [
    Route('/', 'GET', hello),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.main()

It should send a plain text response along with a custom header. Please note that the content¬†should be bytes, not string. That’s why I encoded it.

Moving On

I just walked through some of the features of API Star. There’s a lot more of cool stuff in API Star. I do recommend going through the Github Readme for learning more about different features offered by this excellent framework. I shall also try to cover short, focused tutorials on API Star in the coming days.

Getting Started with Pipenv

If you’re a Python developer, you probably know about pip and the different environment management solutions like virtualenv or venv. The pip tool is currently the standard way to install a Python package. Virtualenv has been a popular way of isolating Python environments for a long time. Pipenv combines the very best of these tools and brings us the one true way to install packages while keeping the dependencies from each project isolated. It claims to have brought the very best from all other packaging worlds (the package manager for other languages / runtimes / frameworks) to the Python world. From what I have seen so far, that claim is quite valid. And it does support Windows pretty well too.

How does Pipenv work?

Pipenv works by creating and managing a virtualenv and a Pipfile for your project. When we install / remove Python packages, the changes are reflected in the Pipfile. It also generates a lock file named Pipfile.lock¬†which is used to lock the version of dependencies and help us produce deterministic builds when needed. In a typical virtualenv¬†setup, we usually create and manage the virtualenv ourselves. Then we activate the environment and pip¬†just installs / uninstalls from that particular virtual environment. Packages like virtualenvwrapper¬†helps us easily create and activate virtualenv¬†with some of it’s handy features. But pipenv¬†takes things further by automating a large part of that. The Pipfile¬†would also make more sense if you have used other packaging systems like Composer, npm, bundler etc.

Getting Started

We need to start by installing pipenv globally. We can install it using pip from PyPi:

pip install pipenv

Now let’s switch to our project directory and try installing a package:

pipenv install flask

When you first run the pipenv install¬†command, you will notice it creates a virtualenv, Pipfile and Pipfile.lock¬†for you. Feel free to go ahead inspect their contents. If you’re using an IDE like PyCharm and want to configure your project interpreter, it would be a good idea to note down the virtualenv¬†path.

Since we have installed Flask, let’s try and running a sample app. Here’s my super simple REST API built with Flask:

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def hello_world():
    return jsonify({"message": "Hello World!"})

Assuming that you have the FLASK_APP environment variable set to app.py (which contains the above code), we can just run the app like this:

pipenv run flask run

Any executables in the current environment can be run using the pipenv run¬†command. But I know what you might be thinking – we want to do just flask¬†run, not use the entire, long command. That’s easy too. We just need to activate the virtualenv with this command:

pipenv shell

Now you can just do flask run¬†or in fact run any executables in the way we’re used to doing.

Handling Dependencies

We can install and uninstall packages using the install¬†and uninstall¬†commands. When we install a new package, it’s added to our Pipfile¬†and the lock file is updated as well. When we uninstall a package, the Pipfile¬†and the lock files are again updated to reflect the change. The update¬†command uninstalls the packages and installs them again so we have the latest updates.

If you would like to check your dependency graph, just use the graph command which will print out the dependencies in a nice format, kind of like this:

PS C:\Users\Masnun\Documents\Python\pipenvtest> pipenv graph
celery==4.1.0
  - billiard [required: >=3.5.0.2,<3.6.0, installed: 3.5.0.3]
  - kombu [required: >=4.0.2,<5.0, installed: 4.1.0]
    - amqp [required: >=2.1.4,<3.0, installed: 2.2.2]
      - vine [required: >=1.1.3, installed: 1.1.4]
  - pytz [required: >dev, installed: 2017.3]
Flask==0.12.2
  - click [required: >=2.0, installed: 6.7]
  - itsdangerous [required: >=0.21, installed: 0.24]
  - Jinja2 [required: >=2.4, installed: 2.10]
    - MarkupSafe [required: >=0.23, installed: 1.0]
  - Werkzeug [required: >=0.7, installed: 0.12.2]

Pipenv is Awesome!

Trust me, it is! It packs a lot of cool and useful features that can help gear up your Python development workflow. There are just too many to cover in a blog post. I would recommend checking out the Pipenv Docs to get familiar with it more.

Deploying A Flask based REST API to AWS Lambda (Serverless) using Zappa

I have heard about AWS Lambda and all the cool things happening in the serverless world. I have also deployed Go functions using the Apex framework for serverless deployment. But recently I have started working on some Python projects again and decided to see how well the Python community is adapting to the serverless era. Not to my surprise, the Python community is doing great as usual. I quickly found an awesome framework named Zappa which makes deploying Python code to AWS Lambda very easy. Python is already natively supported on the AWS Lambda platform. But with the native support you need to configure the API Gateway, S3 bucket and other stuff on your own. Thanks to Zappa, these things are now automated to our convenience. We can easily deploy WSGI apps as well. That means, we can now take our Flask / Django / API Star apps and deploy them to AWS Lambda – with ease and simplicity. In this blog post, I will quickly walk through how to deploy a flask based rest api to the serverless cloud.

Setup Flask App

Before we can get started, we need to create a simple flask app. Here’s a quick rest api (that doesn’t do much):

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def hello_world():
    return jsonify({"message": "Hello World!"})

Let’s save the above code in app.py. We can now install Flask using pip:

pip install flask

And then run the code:

FLASK_APP=app.py flask run

This should run our app and we should be able to visit http://127.0.0.1:5000/ to see the output.

Setting Up AWS

Please make sure you have an account for AWS where you have added your credit card and completed the sign up process. AWS Lambda has 1 million free requests per month which is promised to be always free (not for the first 12 months or anything). When you add your card, you shall also be eligible for a free tier of S3 for 12 months. So you won’t be charged for trying out a sample app deployment. So don’t worry about adding a card. In fact, adding a card is a requirement for getting the free tier.

Once you have your AWS account setup, click on your name (top right) and click “My Security Credentials”. From there, choose the “Access Keys” section and generate a new pair of key and secret. Store them in your ~/.aws/credentials¬†file. AWS Cli (and Zappa) will use these to connect to AWS services and perform required actions. The file should look like this:

[default]
aws_access_key_id=[...]
aws_secret_access_key=[...]

[masnun]
aws_access_key_id=[...]
aws_secret_access_key=[...]

I have created two profiles here. Named profiles are useful if you have more than one accounts/projects/environments to work with. After adding the credentials, add the region information in ~/.aws/config:

[default]
region=us-west-2
output=json

[profile masnun]
region=us-east-2
output=text

This will mostly help with choosing the default region for your app. Once you have these AWS settings configured, you can get started with Zappa.

Install and Configure Zappa

First install Zappa:

pip install zappa

Now cd into the project directory (where our flask app is). Then run:

zappa init

Zappa should guide you through the settings it needs. It should also detect the Flask app and auto complete the app (app.app¬†) for you. Once the wizard finishes, you’re ready to deploy your API.

zappa deploy dev

This should now deploy the app to the dev stage. You can configure different stages for your app in Zappa settings. Once you make some code changes, you can update the app:

zappa update

Both of these commands should print out the url for the app. In my case, the url is:¬†https://1gc1f80kb5.execute-api.us-east-2.amazonaws.com/dev¬†ūüôā

What’s Next?

Congratulations, you just deployed a Flask rest api to AWS Lambda using Zappa. You can make the url shorter by pointing a domain to it from your AWS console. To know more about Zappa and all the great things it can do, please check out Zappa on Github.

Python: Iterators

If you have written some Python code and used the for loop, you have already used iterators behind the scene but you probably didn’t know about it. Iterators are objects that we can iterate over one by one.¬†They are practically everywhere in a Python codebase. Understanding the concepts of iterators and how they work can help us write better, more efficient code from time to time. In this post, we will discuss iterators and other related concepts.

How does iteration work?

Before we can dive into iterators, we first need to understand how iteration works in Python. When we do the for loop, how does Python fetch one item at a time? How does this process work?

There are two functions that come into play – iter¬†and next. The iter¬†function gets an iterator from an object. It actually calls the __iter__¬†special method on the object to get the iterator. So if an object wants to allow iteration, it has to implement the __iter__¬†method. Once it gets the iterator object, it continues to call next¬†on the iterator. The next function in turn calls the __next__¬†method on the iterator object. Let’s see a quick example:

>>> l = [1, 2, 3]

>>> i = iter(l)

>>> type(l)
<class 'list'>

>>> type(i)
<class 'list_iterator'>

>>> next(i)
1

>>> next(i)
2

>>> next(i)
3

>>> next(i)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration


>>>

Let’s see. We first create a list named l¬†with 3 elements. We then call iter()¬†on it. The type of l¬†is list¬†but look at the type of i¬†– it’s list_iterator¬†– interesting! Now we keep calling next¬†on i¬†and it keeps giving us the values we saw in the list, one by one, until there’s a StopIteration¬†exception.

Here the list is an iterable because we can get an iterator from it to iterate over the list. The list_iterator¬†object we got is an iterator, it’s an object that we can actually iterate over. When we loop over a list, this is what happens:

l = [1, 2, 3]

iterator = iter(l)

while True:
    try:
        item = next(iterator)
        print(item)
    except StopIteration:
        break

Makes sens? The for loop actually gets the iterator and keeps looping over until a StopIteration exception is encountered.

Iterator

The iterator is an object which implements __next__¬†method so we can call next¬†on it repeatedly to get the items. Let’s write an iterator that keeps us giving us the next integer, without ever stopping. Let’s name it InfiniteIterator.

class InfiniteIterator:
    def __init__(self):
        self.__int = 0

    def __next__(self):
        self.__int += 1
        return self.__int

If we keep calling next on it, we will keep getting the integers, starting from one.

>>> inf_iter = InfiniteIterator()
>>> next(inf_iter)
1
>>> next(inf_iter)
2
>>> next(inf_iter)
3
>>> next(inf_iter)
4
>>>

Iterable

What if we wanted to create an InfiniteNumbers iterable? It would be such that when we use the for loop on it, it never stops. It keeps producing the next integer in each loop. What would we do? Well, we have an InfiniteIterator. All we need is to define an __iter__ method that returns a new instance of InfiniteIterator.

class InfiniteNumbers:
    def __iter__(self):
        return InfiniteIterator()


infinite_numbers = InfiniteNumbers()

for x in infinite_numbers:
    print(x)

    if x > 99:
        break

If you remove the break statement and the if block, you will notice, it keeps running Рlike forever.

Using StopIteration

Instead of breaking out from our code ourselves, we could use the StopIteration exception in our iterator so it stops after giving us the 100 numbers.

class HundredIterator:
    def __init__(self):
        self.__int = 0

    def __next__(self):
        if self.__int > 99:
            raise StopIteration

        self.__int += 1
        return self.__int


class InfiniteNumbers:
    def __iter__(self):
        return HundredIterator()


one_hundred = InfiniteNumbers()

for x in one_hundred:
    print(x)

Iterators must also implement __iter__

We saw that the __next__¬†method does it’s work just fine. But we also need to implement the __iter__¬†method on an iterator (just like we did in iterable). Why is this required? Let me quote from the official docs:

Iterators are required to have an__iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted.

If we tried to use the for loop over our iterator, it would fail:

class HundredIterator:
    def __init__(self):
        self.__int = 0

    def __next__(self):
        if self.__int > 99:
            raise StopIteration

        self.__int += 1
        return self.__int


one_hundred = HundredIterator()

for x in one_hundred:
    print(x)

We will get the following exception:

Traceback (most recent call last):
  File "iter.py", line 15, in <module>
    for x in one_hundred:
TypeError: 'HundredIterator' object is not iterable

That kind of makes sense because we saw that the for loop runs the iter¬†function on an object to get an iterator from it. Then calls next¬†on the iterator. That’s the problem, we don’t have an __iter__¬†method. The official documentation suggests that every iterator should be a proper iterable too. That is, it should implement the __iter__¬†method and just return an instance of itself. Let’s do that:

class HundredIterator:
    def __init__(self):
        self.__int = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.__int > 99:
            raise StopIteration

        self.__int += 1
        return self.__int


one_hundred = HundredIterator()

for x in one_hundred:
    print(x)

Now the code works fine ūüôā

The Iterator Protocol

The iterator protocol defines the special methods that an object must implement to allow iteration. We can summarize the protocol in this way:

  • Any object that can be iterated over needs to implement the __iter__¬†method which should return an iterator object. Any object that returns an iterator is an iterable.
  • An iterator must implement the __next__¬†method which returns the next item when called. When all items are exhausted (read retrieved), it must raise the StopIteration¬†exception.
  • An iterator must also implement the __iter__¬†method to behave like an iterable.

Why do we need Iterables?

In our last example, we saw that it’s possible for an object to implement a __next__¬†method and an __iter__¬†method that returns self. In this way, an iterator behaves just like an iterable alright. Then why do we need Iterables? Why can’t we just keep using Iterators which refer to itself?

Let’s get back to our HundredIterator¬†example. Once you have iterated over the items once, try to iterate again. What happens? No numbers are output on the screen. Why? Well, because the iterator objects store “state”. Once it has reached StopIteration, it has reached the end line. It’s now exhausted.¬†Every time you call iter¬†on it, it returns the same instace (self) which has nothing more to output.

This is why Iterables are useful. You can just return a fresh instance of an iterator every time the iterable is looped over. This is actually what many built in types like list does.

Why is Iterators so important?

Iterators allow us to consume data each item at a time. Just imagine, if there’s a one GB file and we tried to load it all in memory, it would require huge memory. But what if we implemented an iterator that reads the file one line at a time? We could then just store that one line in memory and do necessary processing before moving on to the next item. This allow us to write really efficient programs ūüôā

This all seems very confusing

If you find the concepts very confusing and hard to grasp, don’t worry. Give it a few tries, write the codes by hand and see the output. Tinker with the examples. Inspect the code, try to see what happens when you modify part of it. All things become easier when you practise more and more. Try writing your own iterables and iterators – perhaps try to clone the built in containers’ functionalities? May be write your own list implementation? Don’t worry, it will come to you in time.

 

Django REST Framework: JSON Web Tokens (JWT)

(This post is a part of a tutorial series on Building REST APIs in Django)

Our last post was about Authentication and Permissions and we covered the available methods of authentication in Django REST Framework. In that post, we learned how to use the built in Token based authentication in DRF. In this post, we will learn more about JSON Web Tokens aka JWT and we will see if JWT can be a better authentication mechanism for securing our REST APIs.

Understanding JSON Web Tokens (JWTs)

We have actually written a detailed blog post about JSON Web Tokens earlier. In case you have missed it, you probably should read it first. We have also described how to use JWT with Flask – reading that one might also help better understand how things work. And of course, we will briefly cover the idea of JWT in this post as well.

If we want to put it simply – you take some data in JSON format, you hash it with a secret and you get a string that you use as a token. You (your web app actually) pass this token to the user when s/he logs in. The user takes the token and on subsequent requests, passes it back in the “Authorization” header. The web app now takes this token back, “decodes” it back to the original JSON payload. It can now read the stored data (identity of the user, token expiry and other data which was embedded in the JSON). While decoding, the same secret is used, so third party attackers can’t just forge a JWT. We would want our token to be small in size, so the JSON payload is usually intentionally kept small. And of course, it should not contain any sensitive information like user password.

JWT vs DRF’s Token Based Authentication

So in our last blog post, we saw Django REST Framework includes a token based authentication system which can generate a token for the user. That works fine, right? Why would we want to switch to JSON Web Tokens instead of that?

Let’s first see how DRF generates the tokens:

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

It’s just¬†random. The token generated can not be anyway related to the user that it belongs to. So how does it associate a token with an user? It stores the token and a reference to the user in a table in database. Here comes the first point – while using DRF’s token based auth, we need to query database on every request (unless of course we have cached that token which). But what if we have multiple application servers?¬†Now we need all our application servers to connect to the same database or same cache server. How will that scale when the project gets really really big? What if we want to provide single sign on across multiple services? We will need to maintain a central auth service where other services request to verify a token. Can JWT simplify these¬†for us?

JWT is just an encoded (read – hashed / signed) JSON data. As long as any webservice has access to the secret used in signing the data, it can also decode and read the embedded data. It doesn’t need any database calls. You can generate the token from one service and other services can read and verify it just fine. It’s more efficient and simply scales better.

JWT in Django REST Framework

DRF does not directly support JWTs out of the box. But there’s an excellent package that adds support for it. Let’s see how easily we can integrate JWT in our REST APIs.

Install and Configure

Let’s first install the package using pip¬†–

pip install djangorestframework-jwt

That should install the package. Now we need to add rest_framework_jwt.authentication.JSONWebTokenAuthentication to the default authentication classes in REST Framework settings.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}

We added it to the top of the list. Next, we just have to add it’s built in view to our urlpatterns.

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = router.urls + [
    url(r'^jwt-auth/', obtain_jwt_token),
]
Obtain a Token

The obtain_jwt_token¬†view will check the user credentials and provide a JWT if everything goes alright. Let’s try it.

$ curl --request POST \
  --url http://localhost:8000/api/jwt-auth/ \
  --header 'content-type: application/json' \
  --data '{"username": "test_user", "password": "awesomepwd"}'

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsInVzZXJuYW1lIjoidGVzdF91c2VyIiwiZXhwIjoxNDk1OTkyOTg2fQ.sWSzdiBNNcXDqhcdcjWKjwpPsVV7tCIie-uit_Yz7W0"}

Awesome, everything worked just fine. We have got our token too. What do we do next? We use this token to access a secured resource.

Using the obtained JWT

We need to pass the token in the form of JWT <token>¬†as the value of the Authorization¬†header. Here’s a sample curl request:

$ curl -H "Content-Type: application/json" -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsInVzZXJuYW1lIjoidGVzdF91c2VyIiwiZXhwIjoxNDk1OTkyOTg2fQ.sWSzdiBNNcXDqhcdcjWKjwpPsVV7tCIie-uit_Yz7W0" -X GET  http://localhost:8000/api/subscribers/

[{"id":1,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":2,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":3,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"},{"id":4,"name":"Abu Ashraf Masnun","age":29,"email":"[email protected]"}]

So our token worked fine! Cool!

Where to go next?

Now that you have seen how simple and easy it is to add JSON Web Token based authentication to Django REST Framework, you probably should dive deeper into the package documentation. Specially these topics might be interesting –

  • Refresh Tokens: If you enable JWT token refreshing, you can exchange your current token with a new, fresh one before¬†the existing one expires. The new token will of course have a renewed expiry time set.
  • Verify Token: If you just share the secret, all services can verify the user on their own. However, in modern micro service based architecture, you may want to provide an API end point that other services can use to verify a JWT they received from the user. This can be useful for those scenarios.
  • And of course look at the settings options available¬†and see how you can customize the token generation process.

In the future, we shall try to cover more about Django, Django REST Framework and Python in general. If you liked the content, please subscribe to the mailing list so we can notify you when we post new contents.

Django REST Framework: Authentication and Permissions

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post about ViewSet, ModelViewSet and Router, we saw how easily we can create REST APIs with the awesome Django REST Framework. In this blog post, we would see how we can secure our endpoints with user authentication and permissions. Authentication will help us identify which user is currently logged in and permissions will decide which user(s) can access which resources.

Authentication

The idea of authentication is pretty simple. When a new incoming request comes, we have to check the request and see if we can identify any user credentials along with it. If you have read the Flask HTTP Auth tutorial or the one about JWT, you might remember how we were checking the authorization header to authenticate our users. We might also receive the user login data via a POST request (form submission) or the user may already be logged in and we can identify using the session data.

We can see that the authentication mechanism can largely vary. Django REST Framework is very flexible in accommodating them. We can give DRF a list of classes, DRF will run the authenticate method on those classes. As soon as a class successfully authenticates the user, the return values from the call is set to request.user and request.auth. If none of the classes manage to authenticate the user, then the user is set to django.contrib.auth.models.AnonymousUser .

We can set these classes using the DEFAULT_AUTHENTICATION_CLASSES¬†settings under the DRF settings. Here’s an example:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

In the example above we used BasicAuthentication and SessionAuthentication Рtwo of the built in classes from Django REST Framework. We will look at how they work and we will also check how we can write our own class for our custom authentication.

(PS: Here we set the authentication policy globally, for all views / paths / resources – if we want, we can also use different authentication mechanism for each one, individually but that is usually not done in most cases).

Basic Authentication

In our example before, we mentioned the BasicAuthentication class. This class first checks the http authorization header (HTTP_AUTHORIZATION in request.META ). If the header contains appropriate string (something like Basic <Base64 Encoded Login>), it will decode the string, split the username, password and try to authenticate the user.

Basic Authentication is very simple, easy to setup and might be quite convenient for testing / debugging but I would highly discourage using this method on production.

Session Authentication

If you have used Django, you already know about session based authentication. In fact, Django itself handles the session based auth and sets the user as part of the request¬†object (an instance of HttpRequest¬†object. DRF just reads the user data from the request and checks for CSRF. That’s it.

Session Authentication works very well if your users are interacting with your API on the web, perhaps using ajax calls? In such cases, if the user is once logged in, his/her auth¬†is stored in the session and we can depend on those data while making requests from our web app. However, this will not work well if the client doesn’t or can not accept cookies (apps on different domains, mobile or desktop apps, other micro services etc).

Token Authentication

If you understand JWT, this one will feel similar, except in this case, the token will be just a “token”, no JSON or no signing. The user logs in and gets a token. On subsequent requests, this token must be passed as part of the authorization header.

To use token based auth, we first need to add the rest_framework.authtoken app to the INSTALLED_APPS list in your settings.py file. And then run the migration to create the related tables.

python manage.py migrate

We also need to add the TokenAuthentication class to our DRF auth class list:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}

Now let’s create a view to issue tokens to user.

from django.contrib.auth import authenticate
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.status import HTTP_401_UNAUTHORIZED
from rest_framework.authtoken.models import Token


@api_view(["POST"])
def login(request):
    username = request.data.get("username")
    password = request.data.get("password")

    user = authenticate(username=username, password=password)
    if not user:
        return Response({"error": "Login failed"}, status=HTTP_401_UNAUTHORIZED)

    token, _ = Token.objects.get_or_create(user=user)
    return Response({"token": token.key})

The code here should be self explanatory. We take username and password. We then try to authenticate the user using Django’s default authentication (checking username and password against what’s stored in the database). If the authentication fails, we return error message along with http status code 401.¬†If the authentication succeeds, we issue a token for the user and pass it in the response.

We need to add this view to our urlpatterns next:

url(r'^login', login)

Now let’s try it out:

$ curl --request POST \
  --url http://localhost:8000/api/login \
  --header 'content-type: application/json' \
  --data '{"username": "test_user", "password": "awesomepwd"}'

{"token":"5e2effff34c85c11a8720a597b96d73a4634c9ad"}%

So we’re getting the tokens successfully. Now to access a secured resource, we need to pass it as part of the authorization header. But how do we make a resource available only to a logged in user? Well, permissions come into play here.

Permissions

While authentication tells us which user is logged in (or not), it’s our responsibility to check if the current user (a valid logged in user or a guest, not logged in visitor) has access to the resource. Permissions¬†can help us deal with that. Just like authentication, we can also set a class of permissions globally or on each resource individually. Let’s start with the IsAuthenticated¬†permission first. Let’s add this to our SubscriberViewSet.

from rest_framework.permissions import IsAuthenticated

class SubscriberViewSet(ModelViewSet):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()
    permission_classes = (IsAuthenticated,)

If we try to access subscribers without any authentication, we will get an error message now:

{
  "detail": "Authentication credentials were not provided."
}

So let’s provide authentication using the token we got.

$ curl -H "Content-Type: application/json" -H "Authorization: Token 5e2effff34c85c11a8720a597b96d73a4634c9ad" http://localhost:8000/api/subscribers/

Now it works fine! There are many useful, already provided permission classes with Django REST Framework. You can find a list of them here http://www.django-rest-framework.org/api-guide/permissions/#api-reference.

Custom Authentication and Permissions

The authentication and permission classes which come with DRF are quite enough for many cases. But what if we needed to create our own? Let’s see how we can do that.

Writing a custom authentication class is very simple. You define your custom authenticate method which would receive the request object. You will have to return an instance of the default User model if authentication succeeds, otherwise raise an exception. You can also return an optional value for the auth object to be set on request. If our authentication method can not be used for this request, we should return None so other classes are tried.

Here’s an example from DRF docs:

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

In this example, the username is being retrieved from a custom header (X_USERNAME) and the rest is quite easy to understand.

Next, let’s see how we can create our custom permission class. For permissions, we can have two types of permissions – global permission or per object permission. Here’s an example of global permission from DRF docs:

from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklisted

If the has_permission¬†method returns True¬†then the user has permission, otherwise not. Let’s see the example for per object permission:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user

For dealing with per object permission, we can override the has_object_permission method. It can take the request, the view and the obj. We have to check if the current user can access the obj in question. Just like before, we need to return True or False to allow or deny the request.

In this blog post, we learned the basics of authentication and permissions. We now know how we can secure our API endpoints with DRF. While the token based authentication was very useful, we kind of like JWT. So in our next post, we will be using a third party package to implement JWT for Django REST Framework.

Django REST Framework: ViewSet, ModelViewSet and Router

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last blog post on ModelSerializer and Generic Views, we demonstrated how we can use the generic views along with ModelSerializer classes to rapidly develop our REST APIs. In this post, we will discuss about ViewSet and ModelViewset and see how they can speed up building APIs even further. At the same time we will take a look at the concepts of Routers which allow us to manage our api routes in a cleaner fashion.

Understanding ViewSet

So far we have learned how we can use the generic views. We can now use them to create the two kinds of resources – “collections” and “elements”. It works very well but we need to write at least two different classes to handle them properly. But if we think about it, both resources focus on the same entity, the same model – “Subscriber”. Wouldn’t it make more sense if we could have all the actions related to a Subscriber in a single class? Wouldn’t that be more convenient if we can put all the Subscriber related logic in a single place?

ViewSets come to the rescue. A ViewSet¬†is, as the name suggests, a class that provides the functionality of a set of views which are closely related. It’s one class but provides a set of views. We can handle both “collection” and “element” type of resources from the same class. And not just those, we can add even other¬†related actions.

Since a ViewSet handles both type of resources, we can no longer think in terms of the http verbs. Because both /api/subscriber and /api/subscriber/1 can respond to  GET requests and should produce different types of response. So we can no longer work with the get, post, put etc methods. We need to think more along the actions we can take on the entity (Subscriber) as a whole. A ViewSet works with these methods instead:

  • list¬†– list all elements, serves GET¬†to /api/subscriber
  • create¬†– create a new element, serves POST¬†to /api/subscriber
  • retrieve¬†– retrieves one element, serves GET¬†to /api/subscriber/1
  • update¬†and partial_update – updates single element, handles PUT/PATCH¬†to /api/subscriber/1
  • destroy¬†– deletes single element, handles DELETE¬†to /api/subscriber/1

Instead of our old get¬†and post¬†methods in separate classes, we can now define these methods in one single class and be done with it. But with general ViewSet, we have to provide logic / code for these views. That would¬†require more time and efforts. What if, we could generate these methods from our models / querysets? Well, we can! ūüėÄ

The ModelViewSet

The ModelViewSet¬†would only ask for the serializer class and the queryset. And then it will provide all the functionality of the different¬†ViewSet¬†methods. Let’s see the example:

from rest_framework.viewsets import ModelViewSet

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberViewSet(ModelViewSet):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

Our SubscriberViewSet now extends the ModelViewSet and we provided the queryset and the serializer_class. Done. Really!

But there’s one slight problem, the ViewSet¬†and ModelViewSet¬†both handle at least two distincts url paths – /api/subscriber¬†(the collection path) and /api/subscriber/1¬†– the elements path. How do we tell Django’s URLConf to route requests to both urls to our single ViewSet? Well, we have to declare those paths ourselves. We can use the as_view¬†to both paths, defining which http verb should be routed to which methods:

SubscriberViewSet.as_view({'get': 'list', 'post':'create'}) # For: /api/subscriber
SubscriberViewSet.as_view({'get': 'retrieve', 'put':'update'}) # For: /api/subscriber/1

Or we can simply use a Router.

Using Routers

A Router¬†instance let’s us register¬†our ViewSet¬†to it and then we can just add the router.urls¬†to our urlpatterns. It will keep track of the view sets we register and the urls¬†property will generate all the url patterns required for those view sets to work. ¬†Let’s add our newly created SubscriberViewSet¬†to a router and see for ourselves. Open the api/urls.py¬†file and create the router there and register the viewset.

from rest_framework.routers import SimpleRouter

from .views import SubscriberViewSet

router = SimpleRouter()
router.register("subscribers", SubscriberViewSet)

urlpatterns = router.urls

That’s all that is required. Now try visiting – http://localhost:8000/api/subscribers or¬†http://localhost:8000/api/subscribers/1 – it all works.

Fantastic, so we just generated a full RESTful API from a model with¬†a surprisingly short amount of code! Isn’t that wonderful? Just don’t trust my words, go and look at your SubscriberViewSet¬†or the SubscriberSerializer¬†or the above defined router.

With the help of ModelSerializer, ModelViewSet and a router instance, we can build elegant CRUD APIs from our Django models insanely fast. At the same time, if we need, we can just override one of those methods (list, retrieve, create etc) to alter the default behavior with our own.

What’s Next?

We have crafted a nice, functional REST API. The next stop would be securing it. In our next post, we will be discussing Authentication and Permissions.

In the mean time, I would request you to subscribe to the mailing list so I can keep you posted about new exciting contents on this site. If you liked the post, please do share with your friends!

 

Django REST Framework: ModelSerializer and Generic Views

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last post on Serializers, we learned how to use Serializers with APIViews. In this post we will discuss how ModelSerializer and the Generic views can take things even further.

Model + Serializer = ModelSerializer

That one line equation is probably enough to explain the concepts of ModelSerializers. In our example, we had to define similar fields on both Serializer and the Model class. We had to write codes for the same fields twice. That is 2x the efforts. ModelSerializers can help solve that problem. A ModelSerializer might remind us of ModelForm. The idea is the same. We extend ModelSerializer and pass it the model. The serializer inspects the model and knows what fields to use and what their types are.

Let’s refactor our old serializer to be a ModelSerializer:

from rest_framework import serializers

from .models import Subscriber


class SubscriberSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscriber
        fields = "__all__"

That’s all we need – our new SubscriberSerializer¬†now infers the fields from the model (Subscriber) we passed to it. We can however choose which fields should be used while serializing / deserializing. In this example we pass the special value “__all__” which means we want to use all fields. But in many cases we would want to selectively use some fields. For example, for Django’s default¬†User¬†model, we don’t want to leak the password¬†data to public, so we will exclude the password¬†field on our User model serializer.

If we try out our API, we would notice everything is working just like before. Except our serializer class is now shorter and more concise. Awesome, right? Let’s move on to our next topic – generic views.

Generic Views

If you have decent amount of experience with Django, you probably have already come across and used the built in generic views. They provide useful functionality around common database operations. For example, we can use the generic list view and provide it with a queryset and the template, it will do the rest for us.

In the same way, we can pass a queryset and serializer to a ListAPIView¬†and it will create the list view for us. Same way we can use CreateAPIView¬†to implement the create view. What’s even better, since we’re using class based views, we can use both of them together, cool, no? Let’s refactor old code to use these two classes.

from rest_framework.generics import ListAPIView, CreateAPIView

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(ListAPIView, CreateAPIView):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

Please note how we no longer need to provide the get and post methods but the API still works. The generic views just know how to deal with those.

There is actually a ListCreateAPIView which is the combination of these two as you can probably understand from the class name. We can just use that one.

from rest_framework.generics import ListCreateAPIView

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(ListCreateAPIView):
    serializer_class = SubscriberSerializer
    queryset = Subscriber.objects.all()

As you can see this generic view is quite helpful for generating “collection” resources (/api/subscriber) easily. There’s also RetrieveUpdateDestroyAPIView which we can use to generate “element” resources. As you can guess from the name, they provide get, put, patch¬†and delete¬†handler for single items (/api/subscriber/12).

Generic views are very handy for quickly generating resources from our models (querysets actually). This allows rapid API development.

What’s Next?

We have seen how we can very quickly create working REST APIs using model serializers and generic views.¬†Things will be even more amazing when we learn about ViewSets, specially ModelViewSets. You will have to wait for our next post for those ūüôā

In case you didn’t notice, we have a mailing list where you can subscribe to get latest updates. We don’t spam, only contents we post. Also if you enjoyed reading the post and found it informative, shouldn’t you share it with your friends? ūüėÄ

Django REST Framework: Serializers

(This post is a part of a tutorial series on Building REST APIs in Django)

In our last blog post, Getting started with Django REST Framework, we saw how we could use the APIView and accept inputs from users using request.data. In our example, we dealt with string, so it was pretty straightforward. But consider the case of age or account_balance Рone has to be integer, the other has to be float / decimal. How do we properly validate the incoming data?

We can manually check every input field and send an error if the field type doesn’t match. But soon we’re going to have a problem at our hand – when the number of inputs will grow, we can’t just keep doing this kind of manual validation. In Django, we would probably use Django Forms for validation. Does DRF provide us with something similar? Yes, it does. The solution to our problem is Serializers.

What can a Serializer do for us?

Have you ever tried JSON serializing a Django model? Or a queryset? You can’t directly because they are not JSON serializable. So what do we do instead? We convert them to Python’s native data structures which could be serialized into JSON. We can serialize querysets into lists and model instances to dictionaries. But doing that by hand is cumbersome.

On the other hand, we saw how we can get incoming data from request.data¬†– we get this data¬†as key value pairs. We can’t just store them in database directly – we have to transform them into Django’s data structures like models and querysets. Doing that by hand is also cumbersome.

Serializers can help us with that. It can serialize complex types into Python natives types and then again deserialize native types into those complex types. Besides that, it also does basic validation based on the serializer field types. If a field is defined as an integer field, it will raise an error if we pass a string to that field. If we need more advanced validation rules, we can plug in the built in Validators¬†or even write our own. Let’s see code examples to understand the use case better.

Defining a Serializer

Create a file named serializers.py inside the api app directory. Put the following codes into it.

from rest_framework import serializers


class HelloWorldSerializer(serializers.Serializer):
    name = serializers.CharField(required=True, max_length=6)
    age = serializers.IntegerField(required=False, min_value=10, default=10)

We’re creating a HelloWorldSerializer¬†which extends serializers.Serializer. We’re defining two fields on this serializer –

  • name¬†is a CharField¬†so it accepts string. It has a max_length¬†of 6.
  • age¬†is an optional integer field. The value must be at least 10 if provided. If not provided, default value will be 10.

With this serializer setup, let’s modify our view to use it.

from .serializers import HelloWorldSerializer


class HelloWorldView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

    def post(self, request):
        serializer = HelloWorldSerializer(data=request.data)
        if serializer.is_valid():
            valid_data = serializer.data

            name = valid_data.get("name")
            age = valid_data.get("age")

            return Response({"message": "Hello {}, you're {} years old".format(name, age)})
        else:
            return Response({"errors": serializer.errors})

We pass the request.data as the data parameter to HelloWorldSerializer so it can read all the request data and parse them. Then we check if the serializer is valid. If you have used Django Forms, this will feel very similar. If the serializer is valid, that means we have a valid set of data available. So we can take the value of name and age and show a pretty message. On the other hand, if the serializer is not valid, we can pass the serializer.errors back to the client, which will contain elaborate error messages.

Let’s try out the API to see what happens. Let’s first send an empty request:

$ curl -H "Content-Type: application/json" -X POST http://localhost:8000/api/hello

{"errors":{"name":["This field is required."]}}

The errors say the name¬†field is required. Of course it is! Let’s pass the name.

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun"}' http://localhost:8000/api/hello

{"message":"Hello masnun, you're 10 years old"}

We just passed the name but didn’t pass the age. Since it is not required and has a default value set, we get the default value. But what if we set a low value?

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": '8'}' http://localhost:8000/api/hello

{"errors":{"age":["Ensure this value is greater than or equal to 10."]}}

So we passed 8¬†and it’s not happy about that. Please note we passed the 8 as a string but DRF doesn’t mind as long as it can convert it to an integer successfully. What if we pass a value that is no number?

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": "ten"}' http://localhost:8000/api/hello

{"errors":{"age":["A valid integer is required."]}}

That works too! Cool, okay then let’s give it a rest and pass a valid value.

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "masnun", "age": 12}' http://localhost:8000/api/hello

{"message":"Hello masnun, you're 12 years old"}

 Serializer with Model

How does Serializers help us in working with models? To understand that, let’s first create one model.

Creating the Subscriber Model

Open api/models.py and add the Subscriber model like this:

class Subscriber(models.Model):
    name = models.CharField("Name", max_length=50)
    age = models.IntegerField("Age")
    email = models.EmailField("Email")

Now create and run the migration.

python manage.py makemigrations
python manage.py migrate

That should setup the table for our new model.

Update The Serializer

We added an email field to our model, also the max length for name is now 50 chars. Let’s update our serializer to match these constraints. Also rename it as SubscriberSerializer.

from rest_framework import serializers


class SubscriberSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=50)
    age = serializers.IntegerField()
    email = serializers.EmailField()
Update The View

And now let’s refactor our view.

from .serializers import SubscriberSerializer
from .models import Subscriber


class SubscriberView(APIView):
    def get(self, request):
        return Response({"message": "Hello World!"})

    def post(self, request):
        serializer = SubscriberSerializer(data=request.data)
        if serializer.is_valid():
            subscriber_instance = Subscriber.objects.create(**serializer.data)
            return Response({"message": "Created subscriber {}".format(subscriber_instance.id)})
        else:
            return Response({"errors": serializer.errors})

The code is very simple and straightforward. If the serializer validation succeeds, we create a new subscriber out of the validated data.

Update URLConf

Let’s update the urls.py¬†to update our url end point.

url(r'^subscriber', SubscriberView.as_view(), name="subscriber")

Now let’s try it out. We will post the following JSON using curl or postman:

{"name": "Abu Ashraf Masnun", "email": "[email protected]", "age": 29}

And we will get back the following response:

{
  "message": "Created subscriber 1"
}

With the serializer, we needed so much less codes. And we did it in a very clean way.

List All Subscribers

According to the REST Best Practices, the GET¬†call to a resource route (/api/subscriber) should return a list of all the items (subscribers). So let’s refactor the get¬†method to return the subscribers list.

    def get(self, request):
        all_subscribers = Subscriber.objects.all()
        serialized_subscribers = SubscriberSerializer(all_subscribers, many=True)
        return Response(serialized_subscribers.data)

We are fetching all subscribers and then passing the queryset to the serializer constructor. Since we’re passing a query set (not just a single model instance rather a list of model instances), we need to set ¬†many=True¬†. Also note,¬†we don’t need to call is_valid¬†– the data is coming from database, they’re already valid. In fact, we can’t call is_valid¬†unless we’re passing some value to the data¬†parameter (SubscriberSerializer(data=request.data)). When we pass queryset or model, the data is automatically available as serializer.data.

What’s Next?

We have so far learned how to use APIView¬†and Serializer¬†to build beautiful APIs with clean code. But we still have some duplication of efforts, for example when we had to define fields on both the model and the serializer. Also, we have to implement both collection and element resources. So that’s like 5 method implementation. Wouldn’t it be great if, by some mechanism we could¬†make things simpler, shorter and cleaner?

We’ll see, in our next blog post ūüôā Please subscribe to our mailing list for email updates. Also if you liked the content, please don’t forget to share with your friends!