Create A Mongodb And PyMongo Webapp Using The Bottle Framework For Python

Introduction

If you’re looking to create a scalable, flexible web application, MongoDB and Python offer a winning combination of tools. In this article, we’ll show you how to create a simple MongoDB web application using the PyMongo driver and the Bottle framework. Even if you don’t have much experience developing web applications, it’s easy to build a MongoDB and PyMongo webapp using the fast, lightweight Bottle framework.

Prerequisites

Let’s go over a few prerequisites and dependencies that need to be in place for this tutorial:

  • We recommend that you use Python 3 to build your webapp since Python 2 has been deprecated.

  • You’ll also need to install some packages, so the PIP3 package manager for Python 3 must be installed and working properly. Use the pip3 -V command in a terminal window to get the version number, or type pip3 list to get a list of all of the packages currently installed.

  • The PyMongo MongoDB client distribution for Python 3 must be installed in order for the web application to make API calls to the MongoDB server. Use the pip3 command to install the package library:

pip3 install pymongo

Install the Bottle package library for Python 3

The simplest way to install Bottle is to use the PIP3 package manager for Python 3:

pip3 install bottle

You can use the UNIX command wget to download the library directly into your project directory:

wget https://bottlepy.org/bottle.py

If you’re on a Debian distribution of Linux, such as Ubuntu or Linux Mint, you can use the apt-get repository:

sudo apt-get install python-bottle

Create a project directory and Python script for the MongoDB Bottle project

Now that we’ve installed and set up everything we need, we can move forward and open our Python script. We’ll import the run and get libraries from the Bottle framework:

# import the run and get methods from the bottle library
from bottle import run, get

We’ll also import the MongoClient class, as well as the errors library for checking the connection to MongoDB:

# import the MongoClient class
from pymongo import MongoClient, errors

Create a client instance of the PyMongo library

After the import statements are complete, the first thing to do is to create a client instance of PyMongo that can connect to a MongoDB server.

Declare global variables for the MongoClient() method

We’ll declare the domain and port variables outside of a function call so that they have a global scope and can be evaluated inside a function:

# declare globals for PyMongo's client host
DOMAIN = "localhost"
PORT = 27017

Use a try-except indentation block to catch any MongoDB server timeout exceptions

Next, we’ll call the PyMongo library’s MongoClient() method inside of a try-except indentation. Be sure to concatenate the domain and port variables as a string before passing them to the method call:

# declare an instance of MongoClient() library
try:
# pass 'host' variables when calling MongoClient()
client = MongoClient(
host = [str(DOMAIN) + ":" + str(PORT)],
serverSelectionTimeoutMS = 3000 # 3 second timeout
)

Call the client’s server_info() method to raise an exception

The client instance’s server_info() method will raise a ServerSelectionTimeoutError exception if the client fails to connect to a MongoDB server at the specified domain and port:

# calling server_info() will raise an exception if client timeout
print ("server_info():", client.server_info()["versionArray"])

except errors.ServerSelectionTimeoutError as err:

# reset the client instance to 'None' in case of timeout exception
client = None

# print pymongo.errors.ServerSelectionTimeoutError
print ("PyMongo ERROR:", err)

The code shown above assigns client a value of None if the client failed to connect to the server. Later in the script, we’ll check this value to see if the connection was successful.

PyMongo failed to connect to MongoDB while running the Bottle server

The reason we need to catch the errors library’s ServerSelectionTimeoutError exception is to verify that the PyMongo client actually connected to the MongoDB server– the client instance won’t indicate this otherwise.

Let’s imagine you accidentally set the port in your Python script to 2701 instead of the default 27017 port number. This try-except indentation allows you to receive a response, alerting you that the port may have been set incorrectly, or the server is otherwise not running.

Screenshot of the Bottle framework returning MongoDB server response as HTML

Define a Python function that will create an HTML string for the Bottle framework

In this section, we’ll declare a function that will concatenate the API data returned by the PyMongo client as an HTML string:

# gef a function that will return HTML string for frontend
def html_from_mongo():

# declare a new string for the HTML
html = '''
<!DOCTYPE html>
<html lang="en">
ObjectRocket Tutorials

'''

Make API calls to the MongoDB server if the client instance is valid

If the client instance created earlier does not have a value of None, we know that the connection was successful, so we can make an API call that will return all of the database names for the server:

# if there's no client then show on frontend
if client != None:
database_names = client.list_database_names()

NOTE: The database_names() method used in earlier versions of PyMongo has been deprecated in favor of list_database_names(). Use the code int(pymongo.version[0]) >= 3 to see if your version of PyMongo is 3.x or newer.

Screenshot of IDLE3 in Python 3 returning version of PyMongo and MongoDB database_names

The method call should return a list of strings containing all of the MongoDB server’s database names.

Iterate over the MongoDB database names and put them into HTML tags

Next, we’ll iterate over the list returned by the list_database_names() method call and append each database name to the html string declared earlier:

for db in database_names:
html += '
Database: '
+ db + '

'

else:
html += '
Warning: MongoDB is not running on'

html += ' port: ' + str(PORT) + '

'


# return the HTML string
return html + "</html>"

The above code puts the names into an HTML tag and uses the style tag for CSS to change the color if the client instance has a value of None. Once the iteration is complete, the function will return the string and pass it to the Bottle framework’s home() function.

Call the Bottle framework’s home() function

In the next bit of code, we’ll call the Bottle framework’s home() function and have it return the concatenated HTML string returned from our MongoDB API call:

@get('/')
def home():
# call the func to return HTML to framework
return html_from_mongo()

The @ Python decorator acts as a pointer, letting Bottle know that the script will run and return the HTML when the user navigates to the site’s home page (/) on the front-end.

Pass the port for the Bottle application to the run() method

We’ll call the Bottle framework’s run() method next. As you follow along with this example, make sure to specify an integer for the port parameter and a host string for your website’s domain. Simply use localhost if you’re testing and developing locally:

# pass a port for the framework's server
run(host='localhost', port=1042, debug=True)

Execute the Python script to start the Bottle server for the MongoDB app

Use the python3 command to run the script for the Bottle framework:

python3 mongo_server.py

Now, open a browser tab and navigate to the port that was passed to the run() method at the end of the script on the server name that was also passed to the method. You should now see some HTML listing your MongoDB collection names:

Screenshot of a webpage loaded by the Python Bottle framwork showing MongoDB collection names

Getting a Socket Bind OSError when running the Bottle Python script

If the port address for the socket is in use, you may need to manually end the process by getting its process ID (PID) and using the kill -9 command in a terminal window to end it:

OSError: [Errno 98] Address already in use
self.socket.bind(self.server_address)

Find all of the processes running on the Bottle port

Use the lsof command to look for all of the PIDs running on the port specified in your Bottle application:

lsof -n -i4TCP:1042

It should list all of the processes by PID under the second column in the text output in the terminal window:

Terminal window screenshot using lsof and kill commands to end a process by its PID

End a process using the ‘kill -9’ command followed by its PID

The following demonstrates how to end a process using kill -9; be sure to replace the example PIDs with the PIDs for your own Python processes:

sudo kill -9 12345
sudo kill -9 54321

Now try to run the MongoDB Bottle server again.

Screenshot of a terminal window running a Bottle server in Python that gets MongoDB database names

Conclusion

It’s clear that the trio of MongoDB, PyMongo and Bottle offer a powerful toolkit for building stable, scalable web applications. In this article, we showed how to build a MongoDB and Pymongo webapp with the help of the Bottle framework. With this example as a guide, you’ll be prepared to build your own webapps using the same tools.

Just the Code

Shown below is the complete script we looked at in our tutorial:

#!/usr/bin/env python3
#-*- coding: utf-8 -*-

# import the run and get methods from the bottle library
from bottle import run, get

# import the MongoClient class
from pymongo import MongoClient, errors

# declare globals for PyMongo's client host
DOMAIN = "localhost"
PORT = 27017

# declare an instance of MongoClient library
try:
# pass 'host' variables when calling MongoClient()
client = MongoClient(
host = [str(DOMAIN) + ":" + str(PORT)],
serverSelectionTimeoutMS = 3000 # 3 second timeout
)

# calling server_info() will raise an exception if client timeout
print ("server_info():", client.server_info()["versionArray"])

except errors.ServerSelectionTimeoutError as err:

# reset the client instance to 'None' in case of timeout exception
client = None

# print pymongo.errors.ServerSelectionTimeoutError
print ("PyMongo ERROR:", err)

# gef a function that will return HTML string for frontend
def html_from_mongo():

# declare a new string for the HTML
html = '''
<!DOCTYPE html>
<html lang="en">

ObjectRocket Tutorials

'''


# if there's no client then show on frontend
if client != None:

# make an
database_names = client.list_database_names()
for db in database_names:
html += '
Database: '
+ db + '

'

else:
html += '
Warning: MongoDB is not running on'

html += ' port: ' + str(PORT) + '

'


# return the HTML string
return html + "</html>"

@get('/')
def home():
# call the func to return HTML to framework
return html_from_mongo()

# pass a port for the framework's server
run(host='localhost', port=1042, debug=True)

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.