HTTP verb
|
Scope
|
Semantics
|
GET
|
Collection of game categories
|
Retrieve all the stored game categories in the collection, sorted by their name in ascending order. Each game category must include a list of URLs for each game resource that belongs to the category.
|
GET
|
Game category
|
Retrieve a single game category. The game category must include a list of URLs for each game resource that belongs to the category.
|
POST
|
Collection of game categories
|
Create a new game category in the collection.
|
PUT
|
Game category
|
Update an existing game category.
|
PATCH
|
Game category
|
Update one or more fields of an existing game category.
|
DELETE
|
Game category
|
Delete an existing... |
Declaring relationships with the models
Make sure you quit the Django's development server. Remember that you just need to press
Ctrl
+
C
in the terminal or command-prompt window in which it is running. Now, we will create the models that we are going to use to represent and persist the game categories, games, players and scores, and their relationships. Open the games/models.py
file and replace its contents with the following code. The lines that declare fields related to other models are highlighted in the code listing. The code file for the sample is included in the restful_python_chapter_02_03
folder.
from django.db import models
class GameCategory(models.Model):
name = models.CharField(max_length=200)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class Game(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=200)
game_category = models.ForeignKey( ...
Managing serialization and deserialization with relationships and hyperlinks
Our new RESTful Web API has to be able to serialize and deserialize the GameCategory
, Game
, Player
, and PlayerScore
instances into JSON representations. In this case, we also have to pay special attention to the relationships between the different models when we create the serializer classes to manage serialization to JSON and deserialization from JSON.
In our last version of the previous API, we created a subclass of the rest_framework.serializers.ModelSerializer
class to make it easier to generate a serializer and reduce boilerplate code. In this case, we will also declare a class that inherits from ModelSerializer
, but the other classes will inherit from the rest_framework.serializers.HyperlinkedModelSerializer
class.
The HyperlinkedModelSerializer
is a type of ModelSerializer
that uses hyperlinked relationships instead of primary key relationships, and therefore, it represents the realationships to other model...
Creating class-based views and using generic classes
This time, we will write our API views by declaring class-based views, instead of function-based views. We might code classes that inherit from the rest_framework.views.APIView
class and declare methods with the same names than the HTTP verbs we want to process: get
, post
, put
, patch
, delete
, and so on. These methods receive a request
argument as happened with the functions that we created for the views. However, this approach would require us to write a lot of code. Instead, we can take advantage of a set of generic views that we can use as our base classes for our class-based views to reduce the required code to the minimum and take advantage of the behavior that has been generalized in Django REST Framework.
We will create subclasses of the two following generic class views declared in rest_framework.generics
:
Taking advantage of generic class based views
Go to the gamesapi/games
folder and open the views.py
file. Replace the code in this file with the following code that declares the required imports and the class based views. We will add more classes to this file later. The code file for the sample is included in the restful_python_chapter_02_03
folder:
from games.models import GameCategory
from games.models import Game
from games.models import Player
from games.models import PlayerScore
from games.serializers import GameCategorySerializer
from games.serializers import GameSerializer
from games.serializers import PlayerSerializer
from games.serializers import PlayerScoreSerializer
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.reverse import reverse
class GameCategoryList(generics.ListCreateAPIView):
queryset = GameCategory.objects.all()
serializer_class = GameCategorySerializer
name = 'gamecategory-list'
...
Working with endpoints for the API
We want to create an endpoint for the root of our API to make it easier to browse the API with the browsable API feature and understand how everything works. Add the following code to the views.py
file to declare the ApiRoot
class. The code file for the sample is included in the restful_python_chapter_02_03
folder.
class ApiRoot(generics.GenericAPIView):
name = 'api-root'
def get(self, request, *args, **kwargs):
return Response({
'players': reverse(PlayerList.name, request=request),
'game-categories': reverse(GameCategoryList.name, request=request),
'games': reverse(GameList.name, request=request),
'scores': reverse(PlayerScoreList.name, request=request)
})
The ApiRoot
class is a subclass of the rest_framework.generics.GenericAPIView
class and declares the get method. The GenericAPIView
class is the base class for all the other generic views. The ApiRoot
class defines the...
Creating and retrieving related resources
Now, we will use the HTTPie command or its curl equivalents to compose and send HTTP requests to the API. We will use JSON for the requests that require additional data. Remember that you can perform the same tasks with your favorite GUI-based tool or with the browsable API.
First, we will compose and send an HTTP request to create a new game category. Remember that we used the browsable API to create a game category named '3D RPG'
.
http POST :8000/game-categories/ name='2D mobile arcade'
The following is the equivalent curl
command:
curl -iX POST -H "Content-Type: application/json" -d '{"name":"2D mobile arcade"}' :8000/game-categories/
The preceding command will compose and send a POST
HTTP request with the specified JSON key-value pair. The request specifies /game-categories/
, and therefore, it will match '^game-categories/$'
and run the post
method for the views.GameCategoryList
class-based view. Remember that the method is defined in the ListCreateAPIView...
In this chapter, we took advantage of the various features included in Django REST Framework that allowed us to eliminate duplicate code and build our API reusing generalized behaviors. We used model serializers, wrappers, default parsing, and rendering options, class based views, and generic classes.
We used the browsable API feature and we designed a RESTful API that interacted with a complex PostgreSQL database. We declared relationships with the models, managed serialization and deserialization with relationships, and hyperlinks. Finally, we created and retrieved related resources and we understood how things work under the hoods.
Now that we have built a complex API with Django REST Framework, we will use additional abstractions included in the framework to improve our API, we will add security and authentication, which is what we are going to discuss in the next chapter.