This tutorial was written by Anaiya Raisinghani (Developer Advocate @ MongoDB)
In this tutorial, we will dive head first into utilizing MongoDB’s Flask-PyMongo library! We will build a basic CRUD (create, read, update, delete) Flask-PyMongo application and learn about how intuitive this library is for building fully functional Flask applications.
What is Flask?
Flask is a very lightweight web application framework where, by design, it makes creating simple applications quick and easy. While it started as a simple wrapper, it has quickly become one of the most popular Python frameworks for developers, as it’s easy to get started but allows for the ability to scale up to complex applications.
Since Flask is a microframework, it does not provide a database abstraction layer and keeps its core simple, but buildable. This is a huge advantage for developers since it’s incredibly malleable: Flask won’t force developers to make big application decisions—the power stays in the hands of the users.
Flask-PyMongo
This extension is so highly regarded for a multitude of reasons. It allows for seamless Flask integration, developers have direct access to PyMongo’s capabilities, and it provides support for advanced MongoDB features.
This library allows for simple configuration. Whereas before, developers needed extra lines of code in order to successfully connect to their MongoDB back end, now, developers can rely on Flask-PyMongo to pick up crucial settings (such as MONGO_URI) from their Flask app configuration. It allows for developers to use the entirety of the PyMongo API, meaning developers can use the full power of PyMongo for all their database needs. It also has a ton of support for more complex features available through MongoDB’s platform.
With all this in mind, let’s jump on in!
Pre-requisities
To be successful in this tutorial, developers will need a variety of pre-requisites:
- A MongoDB Atlas cluster on the free tier. Please make sure to save the connection string someplace safe. Download the sample dataset. We will need it throughout the tutorial. Please make sure the cluster’s IP address is whitelisted for proper connectivity.
- An IDE. This tutorial uses VSCode.
- Python 3.10+.
Please make sure that the name of the sample dataset is in the connection string like such:
mongodb+srv://mongodb:<PASSWORD>@cluster0.qh7j8.mongodb.net/<DATABASE>?retryWrites=true&w=majority&appName=Cluster0
We are using the sample_mflix
dataset for this tutorial.
Step 1: Create a virtual environment
Let’s go ahead and create our virtual environment.
In your root directory, run this command to create a directory called venv
:
python3.12 -m venv venv
Then, activate it:
source venv/bin/activate
Verify the correct Python version is being used:
python —version
This tutorial is working with Python version 3.12.8.
Once the virtual environment is set up, let’s download the Flask-PyMongo
library.
Step 2: Download Flask-PyMongo
In your terminal and in the root directory of your project, run this command:
pip install Flask-PyMongo
When installed, double check to make sure your Flask version is greater than 3.0 and your PyMongo version is greater than 4.0.
Great! Once that’s installed, we can create our app.py
file.
Step 3: Create app.py
file
At the top, we are going to put in our imports. We are using Flask
and render_template
, and we are going to be importing PyMongo
from flask_pymongo
.
from flask import Flask, render_template
from flask_pymongo import PyMongo
We can instantiate our Flask app name, as well:
app = Flask(__name__)
Let’s now copy in our connection string. Please keep in mind this is not encouraged for production, and ensure any sensitive information is properly protected. This is for the sake of the tutorial. For heightened security, please store the connection string in a .env
file.
CONNECTION_STRING = "mongodb+srv://mongodb:<PASSWORD>@cluster0.qh7j8.mongodb.net/sample_mflix?retryWrites=true&w=majority&appName=Cluster0
"
app.config["MONGO_URI"] = CONNECTION_STRING
mongo = PyMongo(app)
Before we create our app.route, let’s first create our index.html
file. Do this by creating a subfolder called templates
and then a file named index.html
.
Here, you can put in any CSS and HTML of your choosing for the homepage to display what we decide to return from our app.route. I am just doing a simple page where we will display the title and plot of some of the movies from our sample_mflix
collection.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Movie List</title>
</head>
<body>
<h1>Movies</h1>
<ul>
{% for movie in movies %}
<li>
<strong>Title:</strong> {{ movie.title }} <br>
<strong>Plot:</strong> {{ movie.plot }}
</li>
{% endfor %}
</ul>
</body>
</html>
Now that we have our index.html
file set up, we can do our app.route
:
@app.route("/")
def home_page():
movies_lookup = mongo.db.movies.find(
{},
{"title": 1, "plot": 1}
).limit(5)
return render_template("index.html", movies=movies_lookup)
if __name__ == '__main__':
app.run(debug=True)
Here, we are returning five movie titles and their plots from our sample_mflix
database and movies
collection.
Once this is saved, run the command:
python app.py
Head over to the link: http://127.0.0.1:5000/.
Troubleshooting: If you are having issues here with connecting, please ensure you have the proper network IP address permissions and that you have incorporated the database name (
sample_mflix
) in your MongoDB connection string when inputting it when prompted.
Step 4: Insert a movie
Let’s insert a new movie into our collection. To do this, we will create a add_movie
form page where the user can insert a movie title and plot of their choosing.
Add in request
at the top of your app.py
file. We will be creating a new @app.route
named /add_movie
for our methods GET
and POST
.
Here is what our route looks like:
@app.route('/add_movie', methods=['GET', 'POST'])
def add_movie():
if request.method == 'POST':
# access our add_movie form
title = request.form['title']
plot = request.form['plot']
# info to create a new movie title and plot
new_movie = {
"title": title,
"plot": plot
}
# insert our new movie
mongo.db.movies.insert_one(new_movie)
# have our success page pop up
return render_template('add_movie_success.html', title=title, plot=plot)
# for GET requests to add_movie, just display the form
return render_template('add_movie.html')
Here is what our add_movie.html
looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add a Movie</title>
</head>
<body>
<h1>Add a New Movie</h1>
<form method="POST" action="{{ url_for('add_movie') }}">
<label for="title">Title:</label>
<input type="text" id="title" name="title" required><br><br>
<label for="plot">Plot:</label>
<textarea id="plot" name="plot" rows="4" cols="50" required></textarea><br><br>
<button type="submit">Add Movie</button>
</form>
</body>
</html>
And our add_movie_success.html
page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Movie Added</title>
</head>
<body>
<h1>Movie added to your collection!</h1>
<p><strong>Title:</strong> {{ title }}</p>
<p><strong>Plot:</strong> {{ plot }}</p>
<a href="{{ url_for('add_movie') }}">Add another movie</a>
</body>
</html>
When we run our application and go to http://127.0.0.1:5000/add_movie,
this is what we see:
Let’s add in a title and plot. For my title, I am just going to do: “Anaiya’s Lyfe”. For my plot, I am going to do: “The life of Anaiya Raisinghani”.
Once entered I will see this:
When we refresh the homepage, we aren’t able to see the new movie. The easiest fix for this is to update our homepage route so that it shows us the five most recently added in movies. Do this by sorting by our ID field. The “-1” indicates sorting by descending order.
@app.route("/")
def home_page():
# we want the 5 most recent movies
movies_lookup = (mongo.db.movies
.find({}, {"title": 1, "plot": 1})
.sort("_id", -1)
.limit(5)
)
return render_template("index.html", movies=movies_lookup)
Now, when we refresh the homepage, we can see our new movie:
But oops! I misspelled the word “Life”. Let’s create a new route so that we can update this movie.
Step 5: Update a document
We are going to do this in a similar manner to when we added in a new movie. We want to create a new route named /movie_update
using the methods GET
and POST
. We will also create two new forms in our templates
folder. It’s best practice to update the document using the “_id”, but for the sake of simplicity and emphasizing the Flask-PyMongo
library, we can update on movie titles. This is also because we are aware there aren’t duplicates in the sample dataset.
Let’s first create the route we want:
@app.route('/update_movie', methods=['GET', 'POST'])
def update_movie():
if request.method == 'POST':
# current title
old_title = request.form['old_title']
new_title = request.form['new_title']
new_plot = request.form['new_plot']
# MongoDB update_one
result = mongo.db.movies.update_one(
{"title": old_title}, # Filter to find the existing document
{"$set": {"title": new_title, "plot": new_plot}} # Fields to update
)
# show us what we changed
return render_template(
'update_movie_success.html',
old_title=old_title,
new_title=new_title,
new_plot=new_plot
)
# for GET requests to update_movie, just display the form
return render_template('update_movie.html')
Our update_movie.html
will look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Update Movie</title>
</head>
<body>
<h1>Update a Movie</h1>
<form action="/update_movie" method="POST">
<label for="old_title">Current Title:</label>
<input type="text" id="old_title" name="old_title" required><br><br>
<label for="new_title">New Title:</label>
<input type="text" id="new_title" name="new_title"><br><br>
<label for="new_plot">New Plot:</label><br>
<textarea id="new_plot" name="new_plot" rows="4" cols="50"></textarea><br><br>
<button type="submit">Update Movie</button>
</form>
</body>
</html>
And our update movie success page will look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Movie Updated</title>
</head>
<body>
<h1>Movie has been updated!</h1>
<p><strong>Old Title:</strong> {{ old_title }}</p>
<p><strong>New Title:</strong> {{ new_title }}</p>
<p><strong>New Plot:</strong> {{ new_plot }}</p>
<a href="{{ url_for('home_page') }}">Go back to Homepage</a>
</body>
</html>
When we head over to http://127.0.0.1:5000/update_movie, we will see this form:
Now, we can put in our current title, “Anaiya’s Lyfe”, and change the title to “Anaiya’s Life”. We can put whatever we like in the plot, or we can keep it the same.
Click on the “Go back to Homepage” button and we will see our change reflected!
Step 6: Delete a document
Now, let’s see how we can delete this movie. I’ve decided I no longer want it in my collection.
We can do this again by creating a new route for delete_movie
.
@app.route('/delete_movie', methods=['GET', 'POST'])
def delete_movie():
if request.method == 'POST':
title = request.form['title']
# using delete_one
mongo.db.movies.delete_one({"title": title})
return render_template('delete_movie_success.html', title=title)
# for GET requests to delete_movie, just display the form
return render_template('delete_movie.html')
Here, we are just rendering a form so that we can put in the title of the movie we want to delete. Then, we are making sure we get a page saying we were successful with deleting the movie.
Here are the form pages.
delete_movie.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Delete Movie</title>
</head>
<body>
<h1>Delete a Movie</h1>
<form action="/delete_movie" method="POST">
<label for="title">Movie Title to Delete:</label>
<input type="text" id="title" name="title" required><br><br>
<button type="submit">Delete Movie</button>
</form>
</body>
</html>
delete_movie_success.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Movie Deleted</title>
</head>
<body>
<h1>Movie has been successfully deleted!</h1>
<p>The movie with title "{{ title }}" has been deleted.</p>
<a href="{{ url_for('home_page') }}">Go back to Homepage</a>
</body>
</html>
Go to http://127.0.0.1:5000/delete_movie.
Now, we can enter in our title, “Anaiya’s Life”, and hit “Delete Movie”.
When your movie has been successfully deleted, you will see this page. Now, click on the homepage and let’s double check that our movie has been removed from the list.
Our movie has been deleted!
Conclusion
In this tutorial, we have successfully created a basic CRUD application using the Flask-PyMongo
library. We have seen how intuitive it is to get up and running with the library!
For more information, please visit the docs.
For any questions, please reach out to us in the Developer Forum.