Book Image

Hands-On RESTful Python Web Services - Second Edition

By : Gaston C. Hillar
1 (1)
Book Image

Hands-On RESTful Python Web Services - Second Edition

1 (1)
By: Gaston C. Hillar

Overview of this book

Python is the language of choice for millions of developers worldwide that builds great web services in RESTful architecture. This second edition of Hands-On RESTful Python Web Services will cover the best tools you can use to build engaging web services. This book shows you how to develop RESTful APIs using the most popular Python frameworks and all the necessary stacks with Python, combined with related libraries and tools. You’ll learn to incorporate all new features of Python 3.7, Flask 1.0.2, Django 2.1, Tornado 5.1, and also a new framework, Pyramid. As you advance through the chapters, you will get to grips with each of these frameworks to build various web services, and be shown use cases and best practices covering when to use a particular framework. You’ll then successfully develop RESTful APIs with all frameworks and understand how each framework processes HTTP requests and routes URLs. You’ll also discover best practices for validation, serialization, and deserialization. In the concluding chapters, you will take advantage of specific features available in certain frameworks such as integrated ORMs, built-in authorization and authentication, and work with asynchronous code. At the end of each framework, you will write tests for RESTful APIs and improve code coverage. By the end of the book, you will have gained a deep understanding of the stacks needed to build RESTful web services.
Table of Contents (19 chapters)
Title Page
Dedication
About Packt
Contributors
Preface
Index

Working with resourceful routing on top of Flask pluggable views


Flask-RESTful uses resources built on top of Flask pluggable views as the main building block for a RESTful API. We just need to create a subclass of the flask_restful.Resource class and declare the methods for each supported HTTP verb.

Note

A subclass of flask_restful.Resource represents a RESTful resource and, therefore, we will have to declare one class to represent the collection of notifications and another one to represent the notification resource.

First, we will create a Notification class that we will use to represent the notification resource. Open the service/service.py file created previously and add the following lines. The code file for the sample is included in the restful_python_2_01_01 folder, in the Flask01/service/service.py file:

class Notification(Resource): 
    def abort_if_notification_not_found(self, id): 
        if id not in notification_manager.notifications: 
            abort( 
                HttpStatus.not_found_404.value,  
                message="Notification {0} doesn't exist".format(id)) 
 
    @marshal_with(notification_fields)def get(self, id): 
        self.abort_if_notification_not_found(id) 
        return notification_manager.get_notification(id) 
 
    def delete(self, id): 
        self.abort_if_notification_not_found(id) 
        notification_manager.delete_notification(id) 
        return '', HttpStatus.no_content_204.value 
 
    @marshal_with(notification_fields)   def patch(self, id): 
        self.abort_if_notification_not_found(id) 
        notification = notification_manager.get_notification(id) 
        parser = reqparse.RequestParser() 
        parser.add_argument('message', type=str) 
        parser.add_argument('ttl', type=int) 
        parser.add_argument('displayed_times', type=int) 
        parser.add_argument('displayed_once', type=bool) 
        args = parser.parse_args() 
        print(args) 
        if 'message' in args and args['message'] is not None: 
            notification.message = args['message'] 
        if 'ttl' in args and args['ttl'] is not None: 
            notification.ttl = args['ttl'] 
        if 'displayed_times' in args and args['displayed_times']
 is not None: 
            notification.displayed_times = args['displayed_times'] 
        if 'displayed_once' in args and args['displayed_once'] is
 not None: 
            notification.displayed_once = args['displayed_once'] 
        return notification 

The Notification class is a subclass of the flask_restful.Resource superclass and declares the following three methods that will be called when the HTTP method with the same name arrives as a request on the represented resource:

  • get: This method receives the ID of the notification that has to be retrieved in the id argument. The code calls the self.abort_if_notification_not_found method to abort in case there is no notification with the requested ID. In case the notification exists, the code returns the NotificationModel instance whose id matches the specified id returned by the notification_manager.get_notification method. The get method uses the @marshal_with decorator, with notification_fields as an argument. The decorator will take the NotificationModel instance and apply the field filtering and output formatting specified in the notification_fields dictionary.
  • delete: This method receives the ID of the notification that has to be deleted in the id argument. The code calls the self.abort_if_notification_not_found method to abort in case there is no notification with the requested ID. In case the notification exists, the code calls the notification_manager.delete_notification method with the received ID as an argument to remove the NotificationModel instance from our data repository. Then, the code returns a tuple composed of an empty response body and a 204 No Content status code. Notice that the returned status code in the tuple is specified with HttpStatus.no_content_204.value because we want to return the value of the enumerable, which is 204. We used multiple return values in the tuple to set the response code.
  • patch: This method receives the ID of the notification that has to be updated or patched in the id argument. The code calls the self.abort_if_notification_not_found method to abort in case there is no notification with the requested ID. In case the notification exists, the code saves the NotificationModel instance whose id matches the specified id returned by the notification_manager.get_notification method in the notification variable. The next line creates a flask_restful.reqparse.RequestParser instance named parser. The RequestParser instance allows us to add arguments with their names and types and then easily parse the arguments received with the request. The code makes four calls to the parser.add_argument method with the argument name and the type of the four arguments we want to parse. Then, the code calls the parser.parse_args method to parse all the arguments from the request and saves the returned dictionary (dict) in the args variable. The code updates all the attributes that have new values in the args dictionary in the NotificationModel instance, which is notification. In case the request didn't include values for certain fields, the code won't make changes to the related attributes because the code doesn't consider the values that are None. The request doesn't need to include the four fields that can be updated with values. The code returns the updated notification. The patch method uses the @marshal_with decorator, with notification_fields as an argument. The decorator will take the NotificationModel instance, notification, and apply the field filtering and output formatting specified in the notification_fields dictionary.

As previously explained, the three methods call the internal abort_if_notification_not_found method, which receives the ID for an existing NotificationModel instance in the id argument. If the received id is not in the keys of the notification_manager.notifications dictionary, the method calls the flask_restful.abort function with HttpStatus.not_found_404.value as the http_status_code argument and a message indicating that the notification with the specified ID doesn't exist. The abort function raises an HTTPException exception for the received http_status_code and attaches the additional keyword arguments to the exception for later processing. In this case, we generate an HTTP 404 Not Found status code.

 

Both the get and patch methods use the @marshal_with decorator, which takes a single data object or a list of data objects, and applies the field filtering and output formatting specified as an argument. The marshalling can also work with dictionaries (dict). In both methods, we specified notification_fields as an argument and, therefore, the code renders the following fields: id, uri, message, ttl, creation_date, notification_category, displayed_times, and displayed_once.

Note

Whenever we use the @marshal_with decorator, we are automatically returning an HTTP 200 OK status code.

The following return statement with the @marshal_with(notification_fields) decorator returns an HTTP 200 OK status code because we didn't specify any status code after the returned object (notification):

return notification 

The next line is the code that is actually executed with the @marshal_with(notification_fields) decorator and we can use it instead of working with the decorator:

return marshal(notification, resource_fields), HttpStatus. HttpStatus.ok_200.value 

For example, we can call the marshal function as shown in the previous line, instead of using the @marshal_with decorator, and the code will produce the same result.

Now, we will create a NotificationList class that we will use to represent the collection of notifications. Open the service/service.py file created previously and add the following lines.

 

 

The code file for the sample is included in the restful_python_2_01_01 folder, in the Flask01/service/service.py file:

class NotificationList(Resource): 
    @marshal_with(notification_fields) 
    def get(self): 
        return [v for v in
notification_manager.notifications.values()] 
 
    @marshal_with(notification_fields) 
    def post(self): 
        parser = reqparse.RequestParser() 
        parser.add_argument('message', type=str, required=True, help='Message cannot be blank!') 
        parser.add_argument('ttl', type=int, required=True,
help='Time to live cannot be blank!') 
        parser.add_argument('notification_category', type=str, required=True, help='Notification category cannot be blank!') 
        args = parser.parse_args() 
        notification = NotificationModel( 
            message=args['message'], 
            ttl=args['ttl'], 
            creation_date=datetime.now(utc), 
            notification_category=args['notification_category'] 
            ) 
        notification_manager.insert_notification(notification)  
        return notification, HttpStatus.created_201.value 

The NotificationList class is a subclass of the flask_restful.Resource superclass and declares the following two methods that will be called when the HTTP method with the same name arrives as a request on the resource represented:

  • get: This method returns a list with all the NotificationModel instances saved in the notification_manager.notifications dictionary. The get method uses the @marshal_with decorator, with notification_fields as an argument. The decorator will take each NotificationModel instance in the returned list and apply the field filtering and output formatting specified in notification_fields.
  • post: This method creates a flask_restful.reqparse.RequestParser instance named parser. The RequestParser instance allows us to add arguments with their names and types and then easily parse the arguments received with the POST request to create a new NotificationModel instance. The code makes three calls to parser.add_argument, with the argument name and the type of the three arguments we want to parse. Then, the code calls the parser.parse_args method to parse all the arguments from the request and saves the returned dictionary (dict) in the args variable. The code uses the parsed arguments in the dictionary to specify the values for the message, ttl, and notification_category attributes to create a new NotificationModel instance and save it in the notification variable. The value for the creation_date argument is set to the current date and time with time zone information, and therefore, it isn't parsed from the request. Then, the code calls the notification_manager.insert_notification method with the new NotificationModel instance (notification) to add this new instance to the dictionary. The post method uses the @marshal_with decorator with notification_fields as an argument. The decorator will take the recently created and stored NotificationModel instance, notification, and apply the field filtering and output formatting specified in notification_fields. Then, the code returns a tuple composed of the inserted NotificationModel instance and a 201 Created status code. Notice that the returned status code in the tuple is specified with HttpStatus.created_201.value because we want to return the value of the enumerable, which is 201. We used multiple return values in the tuple to set the response code.

The following table shows the method of our classes createdpreviously that we want to be executed for each combination of HTTP verb and scope:

HTTP verb

Scope

Class and method

GET

Collection of notifications

NotificationList.get

GET

Notification

Notification.get

POST

Collection of notifications

NotificationList.post

PATCH

Notification

Notification.patch

DELETE

Notification

Notification.delete

Note

If the request results in the invocation of a resource with an unsupported HTTP method, Flask-RESTful will return a response with the HTTP 405 Method Not Allowed status code.