Coding a generic pagination class

Our database has a few rows for each of the tables that persist the models we have defined. However, after we start working with our API in a real-life production environment, we will have hundreds of messages, and therefore, we will have to deal with large result sets. Thus, we will create a generic pagination class and we will use it to easily specify how we want large results sets to be split into individual pages of data.

First, we will compose and send HTTP requests to create 9 messages that belong to one of the categories we have created: Information. This way, we will have a total of 12 messages persisted in the database. We had 3 messages and we add 9 more.

http POST :5000/api/messages/ message='Initializing light controller' duration=25 category="Information"
http POST :5000/api/messages/ message='Initializing light sensor' duration=20 category="Information"
http POST :5000/api/messages/ message='Checking pressure sensor' duration=18 category="Information"
http POST :5000/api/messages/ message='Checking gas sensor' duration=14 category="Information"
http POST :5000/api/messages/ message='Setting ADC resolution' duration=22 category="Information"
http POST :5000/api/messages/ message='Setting sample rate' duration=15 category="Information"
http POST :5000/api/messages/ message='Initializing pressure sensor' duration=18 category="Information"
http POST :5000/api/messages/ message='Initializing gas sensor' duration=16 category="Information"
http POST :5000/api/messages/ message='Initializing proximity sensor' duration=5 category="Information"

The following are the equivalent curl commands:

curl -iX POST -H "Content-Type: application/json" -d '{"message":" Initializing light controller", "duration":25, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Initializing light sensor", "duration":20, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Checking pressure sensor", "duration":18, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Checking gas sensor", "duration":14, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Setting ADC resolution", "duration":22, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Setting sample rate", "duration":15, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Initializing pressure sensor", "duration":18, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Initializing gas sensor", "duration":16, "category": "Information"}' :5000/api/messages/
curl -iX POST -H "Content-Type: application/json" -d '{"message":"Initializing proximity sensor", "duration":5, "category": "Information"}' :5000/api/messages/

The previous commands will compose and send nine POST HTTP requests with the specified JSON key-value pairs. The request specifies /api/messages/, and therefore, it will match '/messages/' and run the MessageListResource.post method, that is, the post method for the MessageListResource class.

Now, we have 12 messages in our database. However, we don't want to retrieve the 12 messages when we compose and send a GET HTTP request to /api/messages/. We will create a customizable generic pagination class to include a maximum of 5 resources in each individual page of data.

Open the api/config.py file and add the following lines that declare two variables that configure the global pagination settings. The code file for the sample is included in the restful_python_chapter_07_01 folder:

PAGINATION_PAGE_SIZE = 5 
PAGINATION_PAGE_ARGUMENT_NAME = 'page' 

The value for the PAGINATION_PAGE_SIZE variable specifies a global setting with the default value for the page size, also known as limit. The value for the PAGINATION_PAGE_ARGUMENT_NAME specifies a global setting with the default value for the argument name that we will use in our requests to specify the page number we want to retrieve.

Create a new helpers.py file within the api folder. The following lines show the code that creates a new PaginationHelper class. The code file for the sample is included in the restful_python_chapter_07_01 folder:

from flask import url_for 
from flask import current_app 
 
 
class PaginationHelper(): 
    def __init__(self, request, query, resource_for_url, key_name, schema): 
        self.request = request 
        self.query = query 
        self.resource_for_url = resource_for_url 
        self.key_name = key_name 
        self.schema = schema 
        self.results_per_page = current_app.config['PAGINATION_PAGE_SIZE'] 
        self.page_argument_name =
        current_app.config['PAGINATION_PAGE_ARGUMENT_NAME'] 
 
    def paginate_query(self): 
        # If no page number is specified, we assume the request wants page #1 
        page_number = self.request.args.get(self.page_argument_name, 1, type=int) 
        paginated_objects = self.query.paginate( 
            page_number, 
            per_page=self.results_per_page, 
            error_out=False) 
        objects = paginated_objects.items 
        if paginated_objects.has_prev: 
            previous_page_url = url_for( 
                self.resource_for_url,  
                page=page_number-1,  
                _external=True) 
        else: 
            previous_page_url = None 
        if paginated_objects.has_next: 
            next_page_url = url_for( 
                self.resource_for_url, 
                page=page_number+1, 
                _external=True) 
        else: 
            next_page_url = None 
        dumped_objects = self.schema.dump(objects, many=True).data 
        return ({ 
            self.key_name: dumped_objects, 
            'previous': previous_page_url, 
            'next': next_page_url, 
            'count': paginated_objects.total 
        }) 

The PaginationHelper class declares a constructor, that is, the __init__ method that received many arguments and uses them to initialize the attributes with the same names:

  • request: The Flask request object that will allow the paginate_query method to retrieve the page number value specified with the HTTP request.
  • query: The SQLAlchemy query that the paginate_query method has to paginate.
  • resource_for_url: A string with the resource name that the paginate_query method will use to generate the full URLs for the previous page and the next page.
  • key_name: A string with the key name that the paginate_query method will use to return the serialized objects.
  • schema: The Flask-Marshmallow Schema subclass that the paginate_query method must use to serialize the objects.

In addition, the constructor reads and saves the values for the configuration variables we added to the config.py file in the results_per_page and page_argument_name attributes.

The class declares the paginate_query method. First, the code retrieves the page number specified in the request and saves it in the page_number variable. In case no page number is specified, the code assumes that request requires the first page. Then, the code calls the self.query.paginate method to retrieve the page number specified by page_number of the paginated result of objects from the database, with a number of results per page indicated by the value of the self.results_per_page attribute. The next line saves the paginated items from the paginated_object.items attribute in the objects variable.

If the value for the paginated_objects.has_prev attribute is True, it means that there is a previous page available. In this case, the code calls the flask.url_for function to generate the full URL for the previous page with the value of the self.resource_for_url attribute. The _external argument is set to True because we want to provide the full URL.

If the value for the paginated_objects.has_next attribute is True, it means that there is a next page available. In this case, the code calls the flask.url_for function to generate the full URL for the next page with the value of the self.resource_for_url attribute.

Then, the code calls the self.schema.dump method to serialize the partial results previously saved in the object variable, with the many argument set to True. The dumped_objects variable saves the reference to the data attribute of the results returned by the call to the dump method.

Finally, the method returns a dictionary with the following key-value pairs:

  • self.key_name: The serialized partial results saved in the dumped_objects variable.
  • 'previous': The full URL for the previous page saved in the previous_page_url variable.
  • 'previous': The full URL for the next page saved in the next_page_url variable.
  • 'count': The total number of objects available in the complete resultset retrieved from the paginated_objects.total attribute.
..................Content has been hidden....................

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