Book Image

Building RESTful Python Web Services

By : Gaston C. Hillar
Book Image

Building RESTful Python Web Services

By: Gaston C. Hillar

Overview of this book

Python is the language of choice for millions of developers worldwide, due to its gentle learning curve as well as its vast applications in day-to-day programming. It serves the purpose of building great web services in the RESTful architecture. This book will show you the best tools you can use to build your own web services. Learn how to develop RESTful APIs using the popular Python frameworks and all the necessary stacks with Python, Django, Flask, and Tornado, combined with related libraries and tools. We will dive deep into each of these frameworks to build various web services, and will provide use cases and best practices on when to use a particular framework to get the best results. We will show you everything required to successfully develop RESTful APIs with the four frameworks such as request handling, URL mapping, serialization, validation, authentication, authorization, versioning, ORMs, databases, custom code for models and views, and asynchronous callbacks. At the end of each framework, we will add authentication and security to the RESTful APIs and prepare tests for it. By the end of the book, you will have a deep understanding of the stacks needed to build RESTful web services.
Table of Contents (18 chapters)
Building RESTful Python Web Services
Credits
About the Author
Acknowledgments
About the Reviewer
www.PacktPub.com
Preface

Managing serialization and deserialization


Our RESTful Web API has to be able to serialize and deserialize the game instances into JSON representations. With Django REST Framework, we just need to create a serializer class for the game instances to manage serialization to JSON and deserialization from JSON.

Django REST Framework uses a two-phase process for serialization. The serializers are mediators between the model instances and Python primitives. Parser and renderers handle as mediators between Python primitives and HTTP requests and responses. We will configure our mediator between the Game model instances and Python primitives by creating a subclass of the rest_framework.serializers.Serializer class to declare the fields and the necessary methods to manage serialization and deserialization. We will repeat some of the information about the fields that we have included in the Game model so that we understand all the things that we can configure in a subclass of the Serializer class. However, we will work with shortcuts that will reduce boilerplate code later in the next examples. We will write less code in the next examples by using the ModelSerializer class.

Now, go to the gamesapi/games folder folder and create a new Python code file named serializers.py. The following lines show the code that declares the new GameSerializer class. The code file for the sample is included in the restful_python_chapter_01_01 folder.

from rest_framework import serializers 
from games.models import Game 
 
 
class GameSerializer(serializers.Serializer): 
    pk = serializers.IntegerField(read_only=True) 
    name = serializers.CharField(max_length=200) 
    release_date = serializers.DateTimeField() 
    game_category = serializers.CharField(max_length=200) 
    played = serializers.BooleanField(required=False) 
 
    def create(self, validated_data): 
        return Game.objects.create(**validated_data) 
 
    def update(self, instance, validated_data): 
        instance.name = validated_data.get('name', instance.name) 
        instance.release_date = validated_data.get('release_date', instance.release_date) 
        instance.game_category = validated_data.get('game_category', instance.game_category) 
        instance.played = validated_data.get('played', instance.played) 
        instance.save() 
        return instance 

The GameSerializer class declares the attributes that represent the fields that we want to be serialized. Notice that they have omitted the created attribute that was present in the Game model. When there is a call to the inherited save method for this class, the overridden create and update methods define how to create or modify an instance. In fact, these methods must be implemented in our class because they just raise a NotImplementedError exception in their base declaration.

The create method receives the validated data in the validated_data argument. The code creates and returns a new Game instance based on the received validated data.

The update method receives an existing Game instance that is being updated and the new validated data in the instance and validated_data arguments. The code updates the values for the attributes of the instance with the updated attribute values retrieved from the validated data, calls the save method for the updated Game instance and returns the updated and saved instance.

We can launch our default Python interactive shell and make all the Django project modules available before it starts. This way, we can check that the serializer works as expected. In addition, it will help us understanding how serialization works in Django. Run the following command to launch the interactive shell. Make sure you are within the gamesapi folder in the Terminal or command prompt:

python manage.py shell

You will notice that a line that says (InteractiveConsole) is displayed after the usual lines that introduce your default Python interactive shell. Enter the following code in the Python interactive shell to import all the things we will need to test the Game model and its serializer. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

from datetime import datetime 
from django.utils import timezone 
from django.utils.six import BytesIO 
from rest_framework.renderers import JSONRenderer 
from rest_framework.parsers import JSONParser 
from games.models import Game 
from games.serializers import GameSerializer 

Enter the following code to create two instances of the Game model and save them. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

gamedatetime = timezone.make_aware(datetime.now(), timezone.get_current_timezone()) 
game1 = Game(name='Smurfs Jungle', release_date=gamedatetime, game_category='2D mobile arcade', played=False) 
game1.save() 
game2 = Game(name='Angry Birds RPG', release_date=gamedatetime, game_category='3D RPG', played=False) 
game2.save() 

After we execute the preceding code, we can check the SQLite database with the previously introduce command-line or GUI tool to check the contents of the games_game table. We will notice the table has two rows and the columns have the values we have provided to the different attributes of the Game instances.

Enter the following commands in the interactive shell to check the values for the primary keys or identifiers for the saved Game instances and the value of the created attribute includes the date and time in which we saved the instance to the database. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

print(game1.pk) 
print(game1.name) 
print(game1.created) 
print(game2.pk) 
print(game2.name) 
print(game2.created) 

Now, let's write the following code to serialize the first game instance (game1). The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

game_serializer1 = GameSerializer(game1) 
print(game_serializer1.data) 

The following line shows the generated dictionary, specifically, a rest_framework.utils.serializer_helpers.ReturnDict instance:

{'release_date': '2016-05-18T03:02:00.776594Z', 'game_category': '2D mobile arcade', 'played': False, 'pk': 2, 'name': 'Smurfs Jungle'} 

Now, let's serialize the second game instance (game2). The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

game_serializer2 = GameSerializer(game2) 
print(game_serializer2.data) 

The following line shows the generated dictionary:

{'release_date': '2016-05-18T03:02:00.776594Z', 'game_category': '3D RPG', 'played': False, 'pk': 3, 'name': 'Angry Birds RPG'} 

We can easily render the dictionaries hold in the data attribute into JSON with the help of the rest_framework.renderers.JSONRenderer class. The following lines create an instance of this class and then calls the render method to render the dictionaries hold in the data attribute into JSON. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

renderer = JSONRenderer() 
rendered_game1 = renderer.render(game_serializer1.data) 
rendered_game2 = renderer.render(game_serializer2.data) 
print(rendered_game1) 
print(rendered_game2) 

The following lines show the output generated from the two calls to the render method:

b'{"pk":2,"name":"Smurfs Jungle","release_date":"2016-05-
    18T03:02:00.776594Z","game_category":"2D mobile arcade","played":false}'
b'{"pk":3,"name":"Angry Birds RPG","release_date":"2016-05-
18T03:02:00.776594Z","game_category":"3D RPG","played":false}'

Now, we will work in the opposite direction: from serialized data to the population of a Game instance. The following lines generate a new Game instance from a JSON string (serialized data), that is, they will deserialize. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file:

json_string_for_new_game = '{"name":"Tomb Raider Extreme Edition","release_date":"2016-05-18T03:02:00.776594Z","game_category":"3D RPG","played":false}' 
json_bytes_for_new_game = bytes(json_string_for_new_game , encoding="UTF-8") 
stream_for_new_game = BytesIO(json_bytes_for_new_game) 
parser = JSONParser() 
parsed_new_game = parser.parse(stream_for_new_game) 
print(parsed_new_game) 

The first line creates a new string with the JSON that defines a new game (json_string_for_new_game). Then, the code converts the string to bytes and saves the results of the conversion in the json_bytes_for_new_game variable. The django.utils.six.BytesIO class provides a buffered I/O implementation using an in-memory bytes buffer. The code uses this class to create a stream from the previously generated JSON bytes with the serialized data, json_bytes_for_new_game, and saves the generated instance in the stream_for_new_game variable.

We can easily deserialize and parse a stream into the Python models with the help of the rest_framework.parsers.JSONParser class. The next line creates an instance of this class and then calls the parse method with stream_for_new_game as an argument, parses the stream into Python native datatypes and saves the results in the parsed_new_game variable.

After executing the preceding lines, parsed_new_game holds a Python dictionary, parsed from the stream. The following lines show the output generated after executing the preceding code snippet:

{'release_date': '2016-05-18T03:02:00.776594Z', 'played': False,
    'game_category': '3D RPG', 'name': 'Tomb Raider Extreme Edition'}

The following lines use the GameSerializer class to generate a fully populated Game instance named new_game from the Python dictionary, parsed from the stream. The code file for the sample is included in the restful_python_chapter_01_01 folder, in the serializers_test_01.py file.

new_game_serializer = GameSerializer(data=parsed_new_game) 
if new_game_serializer.is_valid(): 
    new_game = new_game_serializer.save() 
    print(new_game.name) 

First, the code creates an instance of the GameSerializer class with the Python dictionary that we previously parsed from the stream (parsed_new_game) passed as the data keyword argument. Then, the code calls the is_valid method to determine whether the data is valid. Notice that we must always call is_valid before we attempt to access the serialized data representation when we pass a data keyword argument in the creation of a serializer.

If the method returns true, we can access the serialized representation in the data attribute, and therefore, the code calls the save method that inserts the corresponding row in the database and returns a fully populated Game instance, saved in the new_game local variable. Then, the code prints one of the attributes from the fully populated Game instance. After executing the preceding code, we fully populated two Game instances: new_game1_instance and new_game2_instance.

Tip

As we can learn from the preceding code, Django REST Framework makes it easy to serialize from objects to JSON and deserialize from JSON to objects, which are core requirements for our RESTful Web API that has to perform CRUD operations.

Enter the following command to leave the shell with the Django project modules that we started to test serialization and deserialization:

quit()