How to paginate MongoEngine records in your Python 3 Flask application

Flask and MongoEngine help makes development work easier.

One common task in the development of backend applications is the pagination of database records. Without pagination, the application server can run out of memory while generating a response from the database records.

This post discusses a way to paginate MongoEngine records in your Python 3 Flask application using the facilities provided by the Flask-MongoEngine extension.

Installing flask-mongoengine to your Python 3 environment

pip install flask-mongoengine

Associating an instance of flask_mongoengine.MongoEngine with an instance of Flask

Once our Python 3 environment had included flask-mongoengine, we can proceed to associate an instance of flask_mongoengine.MongoEngine with an instance of Flask in our Python 3 script.

To do so, we can use either the following set of codes:

from flask import Flask
from flask_mongoengine import MongoEngine

app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = MongoEngine(app)

or the following set of codes:

from flask import Flask
from flask_mongoengine import MongoEngine

app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = MongoEngine()
# Init app later
db.init_app(app)

Defining a subclass of flask_mongoengine.Document to represent our MongoDB collection

The next set of codes that we need to write is the definition of a subclass of flask_mongoengine.Document that will represent our MongoDB collection:

# Note: the db variable refers to the flask_mongoengine.MongoEngine 
# instance that we had created earlier.
class Friend(db.Document):
    
    name = db.StringField(max_length=50, required=True)
    phone_number = db.StringField(max_length=50, required=True)
    

Note that we had used the db variable that we can created earlier to get a reference to the flask_mongoengine.Document class. We had also defined the fields that we expect the documents in our MongoDB collection to have.

Calling the paginate function from flask_mongoengine.BaseQuerySet

Once we had defined a subclass of flask_mongoengine.Document, we can then use the following codes to query our MongoDB collection and get a paginated record of the result:

friends_pagination = Friend.objects.paginate(page=1, per_page=10)
# Get information of current pagination session
current_page = friends_pagination.page
total_pages_for_query = friends_pagination.pages
item_per_page = friends_pagination.per_page
total_number_of_items_that_match_query = friends_pagination.total
list_of_items = friends_pagination.items

We first define the page number and items per page via the page and per_page input parameters of the flask_mongoengine.BaseQuerySet.paginate function. The execution of this function will return an instance of flask_mongoengine.pagination.Pagination class that represents a particular page of a MongoDB query that we had indicated.

We can then use the instance of flask_mongoengine.pagination.Pagination to retrieve:

  • the current page number via the page attribute.
  • the total number of pages for the MongoDB query via the pages attribute.
  • the number of items per page via the per_page attribute.
  • the total number of items that are returned for the MongoDB query via the total attribute.
  • the list of items in the current page via the items attribute.

AttributeError: 'QuerySet' object has no attribute 'paginate'?

The paginate function is provided by the BaseQuerySet class inside the flask_mongoengine package which builds on the mongoengine package.

Therefore, there can be a chance where we accidentally define our MongoDB collection representation class with the Document class from the mongoengine package instead of the Document class from the flask_mongoengine package:


import mongoengine as db
class Friend(db.Document):
    
    name = db.StringField(max_length=50, required=True)
    phone_number = db.StringField(max_length=50, required=True)
    

Doing so will result in the call to Friend.objects to return an instance of mongoengine.QuerySet instead. This is why the AttributeError is thrown when we try to call the paginate function.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.