Combining blueprints with resourceful routing

Now, we will create the resources that compose our main building blocks for the RESTful API. First, we will create a few instances that we will use in the different resources. Then, we will create a MessageResource class, that we will use to represent the message resource. Create a new views.py file within the api folder and add the following lines. The code file for the sample is included in the restful_python_chapter_06_01 folder, as shown:

from flask import Blueprint, request, jsonify, make_response 
from flask_restful import Api, Resource 
from models import db, Category, CategorySchema, Message, MessageSchema 
from sqlalchemy.exc import SQLAlchemyError 
import status 
 
 
api_bp = Blueprint('api', __name__) 
category_schema = CategorySchema() 
message_schema = MessageSchema() 
api = Api(api_bp) 
 
 
class MessageResource(Resource): 
    def get(self, id): 
        message = Message.query.get_or_404(id) 
        result = message_schema.dump(message).data 
        return result 
 
    def patch(self, id): 
        message = Message.query.get_or_404(id) 
        message_dict = request.get_json(force=True) 
        if 'message' in message_dict: 
            message.message = message_dict['message'] 
        if 'duration' in message_dict: 
            message.duration = message_dict['duration'] 
        if 'printed_times' in message_dict: 
            message.printed_times = message_dict['printed_times'] 
        if 'printed_once' in message_dict: 
            message.printed_once = message_dict['printed_once'] 
        dumped_message, dump_errors = message_schema.dump(message) 
        if dump_errors: 
            return dump_errors, status.HTTP_400_BAD_REQUEST 
        validate_errors = message_schema.validate(dumped_message) 
        #errors = message_schema.validate(data) 
        if validate_errors: 
            return validate_errors, status.HTTP_400_BAD_REQUEST 
        try: 
            message.update() 
            return self.get(id) 
        except SQLAlchemyError as e: 
                db.session.rollback() 
                resp = jsonify({"error": str(e)}) 
                return resp, status.HTTP_400_BAD_REQUEST 
          
    def delete(self, id): 
        message = Message.query.get_or_404(id) 
        try: 
            delete = message.delete(message) 
            response = make_response() 
            return response, status.HTTP_204_NO_CONTENT 
        except SQLAlchemyError as e: 
                db.session.rollback() 
                resp = jsonify({"error": str(e)}) 
                return resp, status.HTTP_401_UNAUTHORIZED 

The first lines declare the imports and create the following instances that we will use in the different classes:

  • api_bp: It is an instance of the flask.Blueprint class that will allow us to factor the Flask application into this blueprint. The first argument specifies the URL prefix on which we want to register the blueprint: 'api'.
  • category_schema: It is an instance of the CategorySchema class we declared in the models.py module. We will use category_schema to validate, serialize, and deserialize categories.
  • message_schema: It is an instance of the MessageSchema class we declared in the models.py module. We will use message_schema to validate, serialize and, deserialize categories.
  • api: It is an instance of the flask_restful.Api class that represents the main entry point for the application. We pass the previously created flask.Blueprint instance named api_bp as an argument to link the Api to the Blueprint.

The MessageResource class is a subclass of flask_restful.Resource 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 message that has to be retrieved in the id argument. The code calls the Message.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no message with the requested id in the underlying database. In case the message exists, the code calls the message_schema.dump method with the retrieved message as an argument to use the MessageSchema instance to serialize the Message instance whose id matches the specified id. The dump method takes the Message instance and applies the field filtering and output formatting specified in the MessageSchema class. The code returns the data attribute of the result returned by the dump method, that is, the serialized message in JSON format as the body, with the default HTTP 200 OK status code.
  • delete: This method receives the id of the message that has to be deleted in the id argument. The code calls the Message.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no message with the requested id in the underlying database. In case the message exists, the code calls the message.delete method with the retrieved message as an argument to use the Message instance to erase itself from the database. Then, the code returns an empty response body and a 204 No Content status code.
  • patch: This method receives the id of the message that has to be updated or patched in the id argument. The code calls the Message.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no message with the requested id in the underlying database. In case the message exists, the code calls the request.get_json method to retrieve the key-value pairs received as arguments with the request. The code updates specific attributes in case they have new values in the message_dict dictionary in the Message instance: message. Then, the code calls the message_schema.dump method to retrieve any errors generated when serializing the updated message. In case there were errors, the code returns the errors and an HTTP 400 Bad Request status. If the serialization didn't generate errors, the code calls the message_schema.validate method to retrieve any errors generated while validating the updated message. In case there were validation errors, the code returns the validation errors and an HTTP 400 Bad Request status. If the validation is successful, the code calls the update method for the Message instance to persist the changes in the database and returns the results of calling the previously explained self.get method with the id of the updated message as an argument. This way, the method returns the serialized updated message in JSON format as the body, with the default HTTP 200 OK status code.

Now, we will create a MessageListResource class that we will use to represent the collection of messages. Open the previously created api/views.py file and add the following lines. The code file for the sample is included in the restful_python_chapter_06_01 folder:

class MessageListResource(Resource): 
    def get(self): 
        messages = Message.query.all() 
        result = message_schema.dump(messages, many=True).data 
        return result 
 
    def post(self): 
        request_dict = request.get_json() 
        if not request_dict: 
            response = {'message': 'No input data provided'} 
            return response, status.HTTP_400_BAD_REQUEST 
        errors = message_schema.validate(request_dict) 
        if errors: 
            return errors, status.HTTP_400_BAD_REQUEST 
        try: 
            category_name = request_dict['category']['name'] 
            category = Category.query.filter_by(name=category_name).first() 
            if category is None: 
                # Create a new Category 
                category = Category(name=category_name) 
                db.session.add(category) 
            # Now that we are sure we have a category 
            # create a new Message 
            message = Message( 
                message=request_dict['message'], 
                duration=request_dict['duration'], 
                category=category) 
            message.add(message) 
            query = Message.query.get(message.id) 
            result = message_schema.dump(query).data 
            return result, status.HTTP_201_CREATED 
        except SQLAlchemyError as e: 
            db.session.rollback() 
            resp = jsonify({"error": str(e)}) 
            return resp, status.HTTP_400_BAD_REQUEST 

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

  • get: This method returns a list with all the Message instances saved in the database. First, the code calls the Message.query.all method to retrieve all the Message instances persisted in the database. Then, the code calls the message_schema.dump method with the retrieved messages and the many argument set to True to serialize the iterable collection of objects. The dump method will take each Message instance retrieved from the database and apply the field filtering and output formatting specified the MessageSchema class. The code returns the data attribute of the result returned by the dump method, that is, the serialized messages in JSON format as the body with the default HTTP 200 OK status code.
  • post: This method retrieves the key-value pairs received in the JSON body, creates a new Message instance and persists it in the database. In case the specified category name exists, it uses the existing category. Otherwise, the method creates a new Category instance and associates the new message to this new category. First, the code calls the request.get_json method to retrieve the key-value pairs received as arguments with the request. Then, the code calls the message_schema.validate method to validate the new message built with the retrieved key-value pairs. Remember that the MessageSchema class will execute the previously explained process_category method before we call the validate method, and therefore, the data will be processed before the validation takes place. In case there were validation errors, the code returns the validation errors and an HTTP 400 Bad Request status. If the validation is successful, the code retrieves the category name received in the JSON body, specifically in the value for the 'name' key of the 'category' key. Then, the code calls the Category.query.filter_by method to retrieve a category that matches the retrieved category name. If no match is found, the code creates a new Category with the retrieved name and persists in the database. Then, the code creates a new message with the message, duration, and the appropriate Category instance, and persists it in the database. Finally, the code returns the serialized saved message in JSON format as the body, with the HTTP 201 Created status code.

Now, we will create a CategoryResource class that we will use to represent a category resource. Open the previously created api/views.py file and add the following lines. The code file for the sample is included in the restful_python_chapter_06_01 folder:

class CategoryResource(Resource): 
    def get(self, id): 
        category = Category.query.get_or_404(id) 
        result = category_schema.dump(category).data 
        return result 
 
    def patch(self, id): 
        category = Category.query.get_or_404(id) 
        category_dict = request.get_json() 
        if not category_dict: 
            resp = {'message': 'No input data provided'} 
            return resp, status.HTTP_400_BAD_REQUEST 
        errors = category_schema.validate(category_dict) 
        if errors: 
            return errors, status.HTTP_400_BAD_REQUEST 
        try: 
            if 'name' in category_dict: 
                category.name = category_dict['name'] 
            category.update() 
            return self.get(id) 
        except SQLAlchemyError as e: 
                db.session.rollback() 
                resp = jsonify({"error": str(e)}) 
                return resp, status.HTTP_400_BAD_REQUEST 
          
    def delete(self, id): 
        category = Category.query.get_or_404(id) 
        try: 
            category.delete(category) 
            response = make_response() 
            return response, status.HTTP_204_NO_CONTENT 
        except SQLAlchemyError as e: 
                db.session.rollback() 
                resp = jsonify({"error": str(e)}) 
                return resp, status.HTTP_401_UNAUTHORIZED 

The CategoryResource class is a subclass of flask_restful.Resource 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 category that has to be retrieved in the id argument. The code calls the Category.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no category with the requested id in the underlying database. In case the message exists, the code calls the category_schema.dump method with the retrieved category as an argument to use the CategorySchema instance to serialize the Category instance whose id matches the specified id. The dump method takes the Category instance and applies the field filtering and output formatting specified in the CategorySchema class. The code returns the data attribute of the result returned by the dump method, that is, the serialized message in JSON format as the body, with the default HTTP 200 OK status code.
  • patch: This method receives the id of the category that has to be updated or patched in the id argument. The code calls the Category.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no category with the requested id in the underlying database. In case the category exists, the code calls the request.get_json method to retrieve the key-value pairs received as arguments with the request. The code updates just the name attribute in case it has a new value in the category_dict dictionary in the Category instance: category. Then, the code calls the category_schema.validate method to retrieve any errors generated when validating the updated category. In case there were validation errors, the code returns the validation errors and an HTTP 400 Bad Request status. If the validation is successful, the code calls the update method for the Category instance to persist the changes in the database and returns the results of calling the previously explained self.get method with the id of the updated category as an argument. This way, the method returns the serialized updated message in JSON format as the body, with the default HTTP 200 OK status code.
  • delete: This method receives the id of the category that has to be deleted in the id argument. The code calls the Category.query.get_or_404 method to return an HTTP 404 Not Found status in case there is no category with the requested id in the underlying database. In case the category exists, the code calls the category.delete method with the retrieved category as an argument to use the Category instance to erase itself from the database. Then, the code returns an empty response body and a 204 No Content status code.

Now, we will create a CategoryListResource class that we will use to represent the collection of categories. Open the previously created api/views.py file and add the following lines. The code file for the sample is included in the restful_python_chapter_06_01 folder:

class CategoryListResource(Resource): 
    def get(self): 
        categories = Category.query.all() 
        results = category_schema.dump(categories, many=True).data 
        return results 
 
    def post(self): 
        request_dict = request.get_json() 
        if not request_dict: 
            resp = {'message': 'No input data provided'} 
            return resp, status.HTTP_400_BAD_REQUEST 
        errors = category_schema.validate(request_dict) 
        if errors: 
            return errors, status.HTTP_400_BAD_REQUEST 
        try: 
            category = Category(request_dict['name']) 
            category.add(category) 
            query = Category.query.get(category.id) 
            result = category_schema.dump(query).data 
            return result, status.HTTP_201_CREATED 
        except SQLAlchemyError as e: 
            db.session.rollback() 
            resp = jsonify({"error": str(e)}) 
            return resp, status.HTTP_400_BAD_REQUEST 

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

  • get: This method returns a list with all the Category instances saved in the database. First, the code calls the Category.query.all method to retrieve all the Category instances persisted in the database. Then, the code calls the category_schema.dump method with the retrieved messages and the many argument set to True to serialize the iterable collection of objects. The dump method will take each Category instance retrieved from the database and apply the field filtering and output formatting specified the CategorySchema class. The code returns the data attribute of the result returned by the dump method, that is, the serialized messages in JSON format as the body, with the default HTTP 200 OK status code.
  • post: This method retrieves the key-value pairs received in the JSON body, creates a new Category instance and persists it in the database. First, the code calls the request.get_json method to retrieve the key-value pairs received as arguments with the request. Then, the code calls the category_schema.validate method to validate the new category built with the retrieved key-value pairs. In case there were validation errors, the code returns the validation errors and an HTTP 400 Bad Request status. If the validation is successful, the code creates a new category with the specified name, and persists it in the database. Finally, the code returns the serialized saved category in JSON format as the body, with the HTTP 201 Created status code.

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

HTTP verb

Scope

Class and method

GET

Collection of messages

MessageListResource.get

GET

Message

MessageResource.get

POST

Collection of messages

MessageListResource.post

PATCH

Message

MessageResource.patch

DELETE

Message

MessageResource.delete

GET

Collection of categories

CategoryListResource.get

GET

Message

CategoryResource.get

POST

Collection of messages

CategoryListResource.post

PATCH

Message

CategoryResource.patch

DELETE

Message

CategoryResource.delete

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.

We must make the necessary resource routing configurations to call the appropriate methods and pass them all the necessary arguments by defining URL rules. The following lines configure the resource routing for the api. Open the api/views.py file created earlier and add the following lines. The code file for the sample is included in the restful_python_chapter_06_01 folder:

api.add_resource(CategoryListResource, '/categories/') 
api.add_resource(CategoryResource, '/categories/<int:id>') 
api.add_resource(MessageListResource, '/messages/') 
api.add_resource(MessageResource, '/messages/<int:id>') 

Each call to the api.add_resource method routes a URL to a resource, specifically to one of the previously declared subclasses of the flask_restful.Resource class. When there is a request to the API and the URL matches one of the URLs specified in the api.add_resource method, Flask will call the method that matches the HTTP verb in the request for the specified class.

..................Content has been hidden....................

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