Overview
本教程向您展示如何将 Flask应用程序从SQL迁移到MongoDB。具体来说,本教程将使用 SQLAlchemy 的示例博客应用程序转换为使用 Flask- PyMongo 的应用程序。
该示例应用程序包括以下功能:
创建、编辑和查看博客文
搜索博客文
了解MongoDB
MongoDB是一种将数据存储在文档中的 NoSQL数据库。以下列表描述了MongoDB 的关键概念:
集合:文档群组,类似于SQL数据库中的表。集合不实施执行模式。
文档:用于存储数据的类似JSON 的对象。文档可以包含数组和嵌套子文档。
BSON : MongoDB内部使用的二进制JSON格式。 BSON使用日期和二进制等其他数据类型扩展了JSON 。
下表对MongoDB和SQL数据库进行了比较:
功能 | SQL | MongoDB |
|---|---|---|
架构 | 使用预定义的表和列实施固定模式 | 支持动态数据模型的灵活模式 |
数据结构 | 将数据存储在具有行和列的表中 | 将数据存储在文档集合中 |
关系 | 使用外键和联接建立表之间的关系 | 将文档嵌入其他文档或引用其他文档,无需联接 |
查询语言 | 使用结构化查询语言 (SQL) | 使用基于JSON 的丰富查询语言 |
Tutorial
本教程向您展示如何完成以下步骤:
验证先决条件
安装 Flask- PyMongo
更新 Flask应用程序模型
更新 Flask应用程序配置
更新 Flask应用程序路由
从 SQLite 导出数据
将数据导入MongoDB
更新 Flask 模板
运行迁移的应用程序
验证先决条件
本教程需要满足以下先决条件:
MongoDB安装在本地计算机上。有关说明,请参阅MongoDB Server手册中的 安装MongoDB指南。
使用 SQLAlchemy 的现有 Flask应用程序。在本教程中,您可以使用 GitHub 上的flask-mongo存储库中的示例Flask应用程序。
Python 3.7 或更高版本。
flask-mongo示例应用程序包含两个子文件夹:flask-mongo 和 flask-sql。 flask-sql 文件夹包含一个使用 SQLAlchemy 的 Flask应用程序,您可以将其用于本教程中的迁移。
在开始迁移之前,请完成以下操作:
更新 Flask应用程序模型
将 models.py文件的内容替换为以下代码:
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}')"
此代码定义了一个 Post 类,用于对MongoDB 的博文进行建模。该类包括以下方法:
__init__:设置每个帖子的标题、内容、发布日期和唯一标识符to_dict:将实例转换为字典以供MongoDB存储from_dict:从字典创建Post实例__repr__:返回帖子的字符串表示形式
更新 Flask应用程序配置
将 __init__.py文件的内容替换为以下代码:
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
此代码将初始化 Flask应用程序,以通过 Flask- PyMongo扩展使用MongoDB 。
通过添加以下代码来更新 config.py文件以包含MongoDB设置:
import os class Config: SECRET_KEY = os.urandom(24) MONGO_URI = 'mongodb://localhost:27017/blogdb'
删除 manage.py文件。您可以使用此文件在SQL中进行数据库迁移,这不适应用MongoDB。
注意
如果您需要迁移MongoDB,Beanie ODM 可以提供迁移支持。
更新 Flask应用程序路由
将 routes.py文件的内容替换为以下代码:
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)
此代码更新路由以使用MongoDB而不是 SQLAlchemy。这些路由完成以下操作:
使用
to_dict方法将Post实例转换为字典,以便在MongoDB中存储使用以下方法检索帖子
mongo.db.posts.find()使用 插入帖子
mongo.db.posts.insert_one()更新帖子,方法是使用
mongo.db.posts.update_one()删除帖子,方法是使用
mongo.db.posts.delete_one()使用 MongoDB 的正则表达式功能搜索帖子
从 SQLite 导出数据
通过将以下代码添加到新文件,创建名为 export_list_tables.py 的Python脚本以列出 SQLite数据库中的所有表:
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)
运行此脚本以显示表名称。输出显示了 post 表。
创建另一个名为 export_to_json.py 的Python脚本,通过将以下代码添加到新文件来将数据导出为JSON :
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()
运行此脚本以在当前目录中创建 post.json文件。此文件包含JSON格式的导出数据。
将数据导入MongoDB
通过将以下代码添加到新文件中,创建名为 import_to_mongo.py 的Python脚本以将JSON文件导入MongoDB :
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)
运行此脚本以将数据导入MongoDB。该脚本将 date_posted字段转换为日期时间对象,并将所有帖子插入MongoDB集合。
注意
连接字符串mongodb://localhost:27017/blogdb 连接到本地主机上的MongoDB实例的端口 27017。首次写入数据时, MongoDB会自动创建数据库。
其他资源
要学习;了解有关 Flask-Pymongo 的更多信息,请参阅 Flask- PyMongo文档。