Build a Basic CRUD Application With Flask-PyMongo

Anaiya - Feb 27 - - Dev Community

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:

  1. 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.
  2. An IDE. This tutorial uses VSCode.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Then, activate it:

source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Verify the correct Python version is being used:

python —version
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We can instantiate our Flask app name, as well:

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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.

5 movies returned

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')
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

When we run our application and go to http://127.0.0.1:5000/add_movie,
this is what we see:

Form for adding in a new movie

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:

Movie added to collection

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)
Enter fullscreen mode Exit fullscreen mode

Now, when we refresh the homepage, we can see our new movie:

Our new movie has been added to this list showing on our homepage

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')


Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

When we head over to http://127.0.0.1:5000/update_movie, we will see this form:

Update movie 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.

Movie has been updated

Click on the “Go back to Homepage” button and we will see our change reflected!

Movie list with 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')
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Go to http://127.0.0.1:5000/delete_movie.

Delete movie form

Now, we can enter in our title, “Anaiya’s Life”, and hit “Delete Movie”.

Movie was successfully deleted

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.

Movie deleted

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .