Coding a generic pagination class

Right now, the table that persists the notifications in the database has just a few rows. However, after we start working with our API encapsulated in a microservice in a real-life production environment, we will have hundreds of notifications, and therefore, we will have to deal with large result sets. We don't want an HTTP GET request to retrieve 1,000 notifications in a single call. 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 POST requests to create nine notifications that belong to one of the notification categories we have created: Information. This way, we will have a total of 12 messages persisted in the database. We had three messages and we add nine more. The code file for the sample is included in the restful_python_2_03_01 folder, in the Flask01/cmd309.txt file:

http POST ":5000/service/notifications/" message='Clash Royale has a new winner' ttl=25 notification_category='Information'
http POST ":5000/service/notifications/" message='Uncharted 4 has a new 2nd position score' ttl=20 notification_category='Information'
http POST ":5000/service/notifications/" message='Fortnite has a new 4th position score' ttl=18 notification_category='Information'
http POST ":5000/service/notifications/" message='Injustice 2 has a new winner' ttl=14 notification_category='Information'
http POST ":5000/service/notifications/" message='PvZ Garden Warfare 2 has a new winner' ttl=22 notification_category='Information'
http POST ":5000/service/notifications/" message='Madden NFL 19 has a new 3rd position score' ttl=15 notification_category='Information'
http POST ":5000/service/notifications/" message='Madden NFL 19 has a new winner' ttl=18 notification_category='Information'
http POST ":5000/service/notifications/" message='FIFA 19 has a new 3rd position score' ttl=16 notification_category='Information'
http POST ":5000/service/notifications/" message='NBA Live 19 has a new winner' ttl=5 notification_category='Information'
  

The following are the equivalent curl commands. The code file for the sample is included in the restful_python_2_03_01 folder, in the Flask01/cmd310.txt file.

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Clash Royale has a new winner", "ttl":25, "notification_category": "Information"}'
"localhost:5000/service/notifications/"


curl -iX POST -H "Content-Type: application/json" -d '{"message":"Uncharted 4 has a new 2nd position score", "ttl":20, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Fortnite has a new 4th position score", "ttl":18, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Injustice 2 has a new winner", "ttl":14, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"PvZ Garden Warfare 2 has a new winner", "ttl":22, "notification_category": "Information"}'
"localhost:5000/service/notifications/"


curl -iX POST -H "Content-Type: application/json" -d '{"message":"Madden NFL 19 has a new 3rd position score", "ttl":15, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"Madden NFL 19 has a new winner", "ttl":18, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"FIFA 19 has a new 3rd position score", "ttl":16, "notification_category": "Information"}' "localhost:5000/service/notifications/"

curl -iX POST -H "Content-Type: application/json" -d '{"message":"NBA Live 19 has a new winner", "ttl":5, "notification_category": "Information"}'
"localhost:5000/service/notifications/"

The previous commands will compose and send nine HTTP POST requests with the specified JSON key-value pairs. The requests specify /service/notifications/, and therefore, they will match '/notifications/' and run the NotificationListResource.post method, that is, the post method for the NotificationListResource class.

After running the previous commands, we will have 12 notifications persisted in our PostgreSQL database. However, we don't want to retrieve the 12 messages when we compose and send an HTTP GET request to /service/notifications/. We will create a customizable generic pagination class to include a maximum of four resources in each individual page of data.

Open the config.py file within the service folder and add the following lines that declare two variables that configure the global pagination settings.

Open the service/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_2_03_01 folder, in the Flask01/service/config.py file:

PAGINATION_PAGE_SIZE = 4 
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 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 service 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_2_03_01 folder, in the Flask01/service/helpers.py file:

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.page_size =
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 requires 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.page_size, 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, which receives the following 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 page_size 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. If no page number is specified, the code assumes that the 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.page_size 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 objects 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:

Key

Value

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.

'next'

The full URL for the next page saved in the next_page_url variable.

'count'

The total number of objects available in the complete result set 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.141.30.162