Overview
This tutorial shows you how to migrate a Flask application from SQL to MongoDB. Specifically, the tutorial converts an example blog application that uses SQLAlchemy to one that uses Flask-PyMongo.
The example application includes the following features:
Creating, editing, and viewing blog posts
Searching for blog posts
Understanding MongoDB
MongoDB is a NoSQL database that stores data in documents. The following list describes key MongoDB concepts:
Collections: Groups of documents, similar to tables in SQL databases. Collections don't enforce a schema.
Documents: JSON-like objects that store data. Documents can contain arrays and nested subdocuments.
BSON: Binary JSON format that MongoDB uses internally. BSON extends JSON with additional data types such as Date and Binary.
The following table compares MongoDB and SQL databases:
Feature | SQL | MongoDB |
|---|---|---|
Schema | Enforces a fixed schema with predefined tables and columns | Flexible schemas that allow for dynamic data models |
Data Structure | Stores data in tables with rows and columns | Stores data in collections of documents |
Relationships | Uses foreign keys and joins to establish relationships between tables | Embeds documents within other documents or references them, without the need for joins |
Query Language | Uses Structured Query Language (SQL) | Uses a rich, JSON-based query language |
Tutorial
This tutorial shows you how to complete the following steps:
Verify the prerequisites
Install Flask-PyMongo
Update the Flask application models
Update the Flask application configuration
Update the Flask application routes
Export data from SQLite
Import data into MongoDB
Update the Flask templates
Run the migrated application
Verify the prerequisites
This tutorial requires the following prerequisites:
MongoDB installed on your local machine. See the Install MongoDB guide in the MongoDB Server manual for instructions.
An existing Flask application that uses SQLAlchemy. You can use the example Flask application from the flask-mongo repository on GitHub for this tutorial.
Python 3.7 or later.
The flask-mongo example application contains two sub-folders, flask-mongo
and flask-sql. The flask-sql folder contains a Flask application that uses
SQLAlchemy, which you can use for the migration in this tutorial.
Before you begin the migration, complete the following actions:
Review your SQL database schema, including tables, relationships, and data types.
Identify complex queries and relationships that might require special handling in MongoDB.
Design your MongoDB schema considering the document-oriented nature of the database. To learn more about schema design in MongoDB, see the Designing Your Schema guide in the MongoDB Server manual.
Create a complete backup of your current SQL database.
Update the Flask application models
Replace the contents of your models.py file with the
following code:
from datetime import datetime from bson.objectid import ObjectId class Post: def __init__(self, title, content, date_posted=None, _id=None): self.title = title self.content = content self.date_posted = date_posted if date_posted \ else datetime.utcnow() self._id = _id if _id else ObjectId() def to_dict(self): return { "title": self.title, "content": self.content, "date_posted": self.date_posted, "_id": self._id } def from_dict(data): return Post( title=data.get('title'), content=data.get('content'), date_posted=data.get('date_posted'), _id=data.get('_id') ) def __repr__(self): return f"Post('{self.title}', \ '{self.date_posted}')"
This code defines a Post class that models a blog
post for MongoDB. The class includes the following
methods:
__init__: Sets the title, content, date posted, and unique identifier for each postto_dict: Converts an instance into a dictionary for MongoDB storagefrom_dict: Creates aPostinstance from a dictionary__repr__: Returns a string representation of the post
Update the Flask application configuration
Replace the contents of your __init__.py file with
the following code:
from flask import Flask from flask_pymongo import PyMongo from config import Config app = Flask(__name__) app.config.from_object(Config) mongo = PyMongo(app) from app import routes
This code initializes the Flask application to use MongoDB through the Flask-PyMongo extension.
Update your config.py file to include MongoDB
settings by adding the following code:
import os class Config: SECRET_KEY = os.urandom(24) MONGO_URI = 'mongodb://localhost:27017/blogdb'
Delete the manage.py file. You use this file for
database migrations in SQL, which doesn't apply to
MongoDB.
Note
If you need migrations for MongoDB, the Beanie ODM provides migration support.
Update the Flask application routes
Replace the contents of your routes.py file with the
following code:
from flask import render_template, url_for, flash, \ redirect, request from app import app, mongo from bson.objectid import ObjectId from app.models import Post from app.forms import PostForm def home(): posts_data = mongo.db.posts.find() posts = [Post.from_dict(post) for post in posts_data] return render_template("index.html", posts=posts) def new_post(): form = PostForm() if form.validate_on_submit(): post = Post(title=form.title.data, content=form.content.data) mongo.db.posts.insert_one(post.to_dict()) flash("Your post has been created!", "success") return redirect(url_for("home")) return render_template("post.html", title="New Post", form=form) def post(post_id): post_data = mongo.db.posts.find_one_or_404( {"_id": ObjectId(post_id)}) post = Post.from_dict(post_data) return render_template("detail.html", post=post) def edit_post(post_id): post_data = mongo.db.posts.find_one_or_404( {"_id": ObjectId(post_id)}) post = Post.from_dict(post_data) form = PostForm() if form.validate_on_submit(): updated_post = { "$set": {"title": form.title.data, "content": form.content.data} } mongo.db.posts.update_one( {"_id": ObjectId(post_id)}, updated_post) flash("Your post has been updated!", "success") return redirect(url_for("post", post_id=post_id)) elif request.method == "GET": form.title.data = post.title form.content.data = post.content return render_template("edit_post.html", title="Edit Post", form=form) def delete_post(post_id): mongo.db.posts.delete_one({"_id": ObjectId(post_id)}) flash("Your post has been deleted!", "success") return redirect(url_for("home")) def search(): query = request.args.get("query") posts_data = mongo.db.posts.find( { "$or": [ {"title": {"$regex": query, "$options": "i"}}, {"content": {"$regex": query, "$options": "i"}}, ] } ) posts = [Post.from_dict(post) for post in posts_data] return render_template("index.html", posts=posts)
This code updates the routes to use MongoDB instead of SQLAlchemy. The routes complete the following actions:
Convert
Postinstances to dictionaries by using theto_dictmethod for storage in MongoDBRetrieve posts by using
mongo.db.posts.find()Insert posts by using
mongo.db.posts.insert_one()Update posts by using
mongo.db.posts.update_one()Delete posts by using
mongo.db.posts.delete_one()Search posts by using MongoDB's regex capabilities
Export data from SQLite
Create a Python script named export_list_tables.py to list all tables
in your SQLite database by adding the following code to a new file:
import sqlite3 # Connect to the SQLite database db_path = 'relative/path/to/site.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() # Query to get all table names cursor.execute("SELECT name FROM sqlite_master WHERE \ type='table';") tables = cursor.fetchall() # Close the connection conn.close() print(tables)
Run this script to display your table names. The output
shows the post table.
Create another Python script named export_to_json.py to export the
data to JSON by adding the following code to a new file:
import sqlite3 import json # Connect to the SQLite database db_path = 'relative/path/to/site.db' conn = sqlite3.connect(db_path) cursor = conn.cursor() # Export data from the 'post' table cursor.execute("SELECT * FROM post") rows = cursor.fetchall() # Get the column names column_names = [description[0] for description in \ cursor.description] # Convert to list of dictionaries data = [dict(zip(column_names, row)) for row in rows] # Save to a JSON file with open('post.json', 'w') as f: json.dump(data, f, indent=4) # Close the connection conn.close()
Run this script to create the post.json file in the current directory.
This file contains your exported data in JSON format.
Import data into MongoDB
Create a Python script named import_to_mongo.py to import the
JSON file into MongoDB by adding the following code to a new file:
from pymongo import MongoClient import json from datetime import datetime def import_json_to_mongo(db_name, collection_name, json_path): client = MongoClient("mongodb://localhost:27017/") db = client[db_name] def convert_date(data): # Update 'date_posted' field to datetime object for item in data: if 'date_posted' in item: item['date_posted'] = datetime.strptime( item['date_posted'], '%Y-%m-%d %H:%M:%S.%f') return data with open(json_path, 'r') as f: data = json.load(f) data = convert_date(data) db[collection_name].insert_many(data) client.close() if __name__ == "__main__": db_name = 'blogdb' collection_name = 'posts' json_path = 'flask-mongo/post.json' import_json_to_mongo(db_name, collection_name, json_path)
Run this script to import your data into MongoDB. The
script converts the date_posted field to a datetime
object and inserts all posts into the MongoDB collection.
Note
The connection string
mongodb://localhost:27017/blogdb connects to your
MongoDB instance on localhost at port 27017. MongoDB
creates the database automatically when you first
write data.
Additional Resources
To learn more about Flask-Pymongo, see the Flask-PyMongo documentation.