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 typepip3 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:
1 | 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:
1 | pip3 install bottle |
You can use the UNIX command wget
to download the library directly into your project directory:
1 | 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:
1 | 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:
1 2 | # 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:
1 2 | # 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:
1 2 3 | # 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:
1 2 3 4 5 6 7 | # 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:
1 2 3 4 5 6 7 8 9 10 | # 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.
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:
1 2 3 4 5 6 7 8 9 10 | # 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:
1 2 3 | # 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.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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:
1 2 3 4 | @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:
1 2 | # 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:
1 | 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:
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:
1 2 | 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:
1 | 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:
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:
1 2 | sudo kill -9 12345 sudo kill -9 54321 |
Now try to run the MongoDB Bottle server again.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #!/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