Writing API views

Now we will create Django views that will use the previously created GameSerializer class to return JSON representations for each HTTP request that our API will handle. Open the views.py file located within the games_service/games folder. The following lines show the initial code for this file, with just one import statement and a comment that indicates we should create the views:

from django.shortcuts import render 
 
# Create your views here. 

Replace the existing code with the following lines. The new code creates a JSONResponse class and declares two functions: game_collection and game_detail. We are creating our first version of the API, and we use functions to keep the code as simple as possible. We will work with classes and more complex code in the next examples. The highlighted lines show the expressions that evaluate the value of the request.method attribute to determine the actions to be performed based on the HTTP verb. The code file for the sample is included in the restful_python_2_05_01 folder, in the Django01/games-service/games/views.py file:

from django.http import HttpResponse 
from django.views.decorators.csrf import csrf_exempt 
from rest_framework.renderers import JSONRenderer 
from rest_framework.parsers import JSONParser 
from rest_framework import status 
from games.models import Game 
from games.serializers import GameSerializer 
 
 
class JSONResponse(HttpResponse): 
    def __init__(self, data, **kwargs): 
        content = JSONRenderer().render(data) 
        kwargs['content_type'] = 'application/json' 
        super(JSONResponse, self).__init__(content, **kwargs) 
 
 
@csrf_exempt 
def game_collection(request): 
    if request.method == 'GET': 
        games = Game.objects.all() 
        games_serializer = GameSerializer(games, many=True) 
        return JSONResponse(games_serializer.data) 
    elif request.method == 'POST': 
        game_data = JSONParser().parse(request) 
        game_serializer = GameSerializer(data=game_data) 
        if game_serializer.is_valid(): 
            game_serializer.save() 
            return JSONResponse(game_serializer.data,  
                status=status.HTTP_201_CREATED) 
        return JSONResponse(game_serializer.errors,  
            status=status.HTTP_400_BAD_REQUEST) 
 
 
@csrf_exempt 
def game_detail(request, id): 
    try: 
        game = Game.objects.get(id=id) 
    except Game.DoesNotExist: 
        return HttpResponse(status=status.HTTP_404_NOT_FOUND) 
    if request.method == 'GET': 
        game_serializer = GameSerializer(game) 
        return JSONResponse(game_serializer.data) 
    elif request.method == 'PUT': 
        game_data = JSONParser().parse(request) 
        game_serializer = GameSerializer(game,  
            data=game_data) 
        if game_serializer.is_valid(): 
            game_serializer.save() 
            return JSONResponse(game_serializer.data) 
        return JSONResponse(game_serializer.errors,  
            status=status.HTTP_400_BAD_REQUEST) 
    elif request.method == 'DELETE': 
        game.delete() 
        return HttpResponse(status=status.HTTP_204_NO_CONTENT) 

The JSONResponse class is a subclass of the django.http.HttpResponse class. The superclass represents an HTTP response with a string as content. The JSONResponse class renders its content into JSON. The class defines just declare the __init__ method that created a rest_framework.renderers.JSONRenderer instance and calls its render method to render the received data into JSON and save the returned bytestring in the content local variable. Then, the code adds the 'content_type' key to the response header with 'application/json' as its value. Finally, the code calls the initializer for the base class with the JSON bytestring and the key-value pair added to the header. This way, the class represents a JSON response that we use in the two functions to easily return a JSON response.

The code uses the @csrf_exempt decorator in the two functions to ensure that the view sets a CSRF (short for Cross-Site Request Forgery) cookie. We do this to make it simple to test this example that doesn't represent a production-ready web service. We will add security features to our RESTful API later.

When the Django server receives an HTTP request, Django creates an HttpRequest instance, specifically a django.http.HttpRequest object. This instance contains metadata about the request, including the HTTP verb. The method attribute provides a string representing the HTTP verb or method used in the request.

When Django loads the appropriate view that will process the requests, it passes the HttpRequest instance as the first argument to the view function. The view function has to return an HttpResponse instance, specifically, a django.http.HttpResponse instance.

The game_collection function lists all the games or creates a new game. The function receives an HttpRequest instance in the request argument. The function is capable of processing two HTTP verbs: GET and POST. The code checks the value of the request.method attribute to determine the code to be executed based on the HTTP verb. If the HTTP verb is GET, the request.method == 'GET' expression will evaluate to True and the code has to list all the games. The code will retrieve all the Game objects from the database, use the GameSerializer to serialize all of them and return a JSONResponse instance built with the data generated by GameSerializer. The code creates the GameSerializer instance with the many=True argument to specify that multiple instances have to be serialized and not just one. Under the hood, Django uses a ListSerializer when the many argument value is set to True.

If the HTTP verb is POST, the code has to create a new game based on the JSON data that is included in the HTTP request. First, the code uses a JSONParser instance and calls its parse method with request as an argument to parse the game data provided as JSON data in the request and saves the results in the game_data local variable. Then, the code creates a GameSerializer instance with the previously retrieved data and calls the is_valid method to determine whether the Game instance is valid or not. If the instance is valid, the code calls the save method to persist the instance in the database and returns a JSONResponse with the saved data in its body and a status equal to status.HTTP_201_CREATED, that is, 201 Created.

The game_detail function retrieves, updates, or deletes an existing game. The function receives an HttpRequest instance in the request argument and the ID for the game to be retrieved, updated, or deleted in the id argument. The function is capable of processing three HTTP verbs: GET, PUT, and DELETE. The code checks the value of the request.method attribute to determine the code to be executed based on the HTTP verb. Irrespective of the HTTP verb, the function calls the Game.objects.get method with the received id as the id argument to retrieve a Game instance from the database based on the specified id, and saves it in the game local variable. If a game with the specified id doesn't exist in the database, the code returns an HttpResponse with its status equal to status.HTTP_404_NOT_FOUND, that is, 404 Not Found.

If the HTTP verb is GET, the code creates a GameSerializer instance with game as an argument and returns the data for the serialized game in a JSONResponse that will include the default 200 OK status. The code returns the retrieved game serialized as JSON.

If the HTTP verb is PUT, the code has to create a new game based on the JSON data that is included in the HTTP request, and use it to replace an existing game. First, the code uses a JSONParser instance and calls its parse method with request as an argument to parse the game data provided as JSON data in the request and saves the results in the game_data local variable. Then, the code creates a GameSerializer instance with the Game instance previously retrieved from the database, game, and the retrieved data that will replace the existing data, game_data. Then, the code calls the is_valid method to determine whether the Game instance is valid or not. If the instance is valid, the code calls the save method to persist the instance with the replaced values in the database and returns a JSONResponse with the saved data in its body and the default 200 OK status. If the parsed data doesn't generate a valid Game instance, the code returns a JSONResponse with a status equal to status.HTTP_400_BAD_REQUEST, that is, 400 Bad Request.

If the HTTP verb is DELETE, the code calls the delete method for the Game instance previously retrieved from the database (game). The call to the delete method erases the underlying row in the games_game table, and therefore, the game won't be available anymore. Then, the code returns a JSONResponse with a status equal to status.HTTP_204_NO_CONTENT that is, 204 No Content.

Now we have to create a new Python file named urls.py in the games_service/games folder, specifically, the games_service/games/urls.py file. The following lines show the code for this file that defines the URL patterns that specifies the regular expressions that have to be matched in the request to run a specific function defined in the views.py file. The code file for the sample is included in the restful_python_2_05_01 folder, in the Django01/games-service/games/urls.py file:

from django.conf.urls import url 
from games import views 
 
urlpatterns = [ 
    url(r'^games/$', views.game_collection), 
    url(r'^games/(?P<id>[0-9]+)/$', views.game_detail), 
] 

The urlpatterns list makes it possible to route URLs to views. The code calls the django.conf.urls.url function with the regular expression that has to be matched, and the view function defined in the views module as arguments to create a RegexURLPattern instance for each entry in the urlpatterns list.

Now we have to replace the code in the urls.py that Django built automatically in the games_service folder, specifically, the games_service/urls.py file. Don't confuse this file with the previously created urls.py file that is saved in another folder. The games_service/urls.py file defines the root URL configurations, and therefore, we must include the URL patterns declared in the previously coded games_service/games/urls.py file. The following lines show the new code for the games_service/urls.py file. The code file for the sample is included in the restful_python_2_05_01 folder, in the Django01/games-service/urls.py file:

from django.conf.urls import url, include 
 
urlpatterns = [ 
    url(r'^', include('games.urls')), 
] 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.22.135