Using SQLAlchemy with Flask and PostgreSQL
介绍
数据库是现代应用程序中至关重要的部分,因为它们存储了用于为其提供动力的数据。 通常,我们使用结构化查询语言(SQL)对数据库执行查询并处理其中的数据。 尽管最初是通过专用的SQL工具完成的,但是我们已经迅速转移到在应用程序内部使用SQL来执行查询。
自然地,随着时间的流逝,对象关系映射器(ORM)诞生了,这使我们能够安全,轻松,方便地以编程方式连接到我们的数据库,而无需实际运行查询来处理数据。
一种这样的ORM是SQLAlchemy。 在本文中,我们将更深入地研究ORM,尤其是SQLAlchemy,然后使用它来使用Flask框架来构建数据库驱动的Web应用程序。
什么是ORM,为什么要使用它?
顾名思义,对象关系映射将对象映射到关系实体。 在面向对象的编程语言中,对象与关系实体没有什么不同-它们具有可以互换地映射的某些字段/属性。
话虽如此,因为将对象映射到数据库相当容易,所以反向操作也非常简单。 这简化了软件开发过程,并减少了在编写普通SQL代码时出现人为错误的机会。
使用ORM的另一个优点是,通过允许我们使用模型来操纵数据,而不是每次需要访问数据库时都编写SQL代码,它们可以帮助我们编写符合DRY(不要重复自己)原则的代码。
ORM从我们的应用程序中提取数据库,使我们能够轻松使用多个数据库或切换数据库。 假设,如果我们在应用程序中使用SQL连接到MySQL数据库,则由于要更改其语法,因此如果要切换到MSSQL数据库,则需要修改代码。
如果我们的SQL在我们的应用程序中的多个点集成在一起,这将被证明很麻烦。 通过ORM,我们需要进行的更改将仅限于更改几个配置参数。
尽管ORM通过抽象化数据库操作使我们的生活更轻松,但我们仍需注意不要忘记幕后发生的事情,因为这也将指导我们如何使用ORM。 我们还需要熟悉ORM并学习它们,以便更有效地使用它们,这会引入一些学习曲线。
SQLAlchemy ORM
SQLAlchemy是用Python编写的ORM,可为开发人员提供SQL的功能和灵活性,而无需真正使用它。
SQLAlchemy环绕了Python附带的Python数据库API(Python DBAPI),其创建目的是为了促进Python模块与数据库之间的交互。
创建DBAPI是为了建立数据库管理的一致性和可移植性,尽管我们不需要直接与它进行交互,因为SQLAlchemy将是我们的联系点。
还需要注意的是,SQLAlchemy ORM是建立在SQLAlchemy Core之上的-SQLAlchemy Core处理DBAPI集成并实现SQL。 换句话说,SQLAlchemy Core提供了生成SQL查询的方法。
尽管SQLAlchemy ORM使我们的应用程序与数据库无关,但必须注意,特定的数据库将需要特定的驱动程序才能连接到它们。 一个很好的例子是Pyscopg,它是DBAPI的PostgreSQL实现,当与SQLAlchemy结合使用时,它允许我们与Postgres数据库进行交互。
对于MySQL数据库,PyMySQL库提供了与之交互所需的DBAPI实现。
SQLAlchemy也可以与Oracle和Microsoft SQL Server一起使用。 行业中一些依赖SQLAlchemy的知名企业包括Reddit,Yelp,DropBox和Survey Monkey。
介绍了ORM之后,让我们构建一个简单的Flask API,该API与Postgres数据库进行交互。
带SQLAlchemy的烧瓶
Flask是一个轻量级的微框架,用于构建最少的Web应用程序,并且通过第三方库,我们可以利用其灵活性来构建健壮且功能丰富的Web应用程序。
在我们的案例中,我们将构建一个简单的RESTful API,并使用Flask-SQLAlchemy扩展将我们的API连接到Postgres数据库。
先决条件
我们将使用PostgreSQL(也称为Postgres)来存储将由API处理和操纵的数据。
要与我们的Postgres数据库进行交互,我们可以使用命令行或带有图形用户界面的客户端,使它们更易于使用并且导航更快。
对于Mac OS,我建议您使用Postico,它非常简单直观,并提供干净的用户界面。
PgAdmin是另一个出色的客户端,支持所有主要操作系统,甚至提供Dockerized版本。
我们将使用这些客户端来创建数据库,并在应用程序的开发和执行期间查看数据。
随着安装的进行,让我们创建环境并安装应用程序所需的依赖项:
1 2 3 4 5 | $ virtualenv --python=python3 env --no-site-packages $ source env/bin/activate $ pip install psycopg2-binary $ pip install flask-sqlalchemy $ pip install Flask-Migrate |
上面的命令将创建并激活virtualenv,安装Psycopg2驱动程序,安装flask-sqlalchemy,并安装Flask-Migrate来处理数据库迁移。
在我们的情况下,我们不必每次应用程序启动时都重新创建数据库或表,并且在不存在的情况下将自动为我们执行此操作。
实作
我们将构建一个简单的API来处理和操纵有关汽车的信息。 数据将存储在PostgreSQL数据库中,并且通过API我们将执行CRUD操作。
首先,我们必须使用我们选择的PostgreSQL客户端创建
放置好数据库后,让我们连接它。 我们将从在
1 2 3 4 5 6 7 8 9 10 | from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return {"hello":"world"} if __name__ == '__main__': app.run(debug=True) |
我们首先创建一个Flask应用程序和一个返回JSON对象的端点。
对于我们的演示,我们将使用Flask-SQLAlchemy,这是专门用于向Flask应用程序添加SQLAlchemy功能的扩展。
现在让我们将Flask-SQLAlchemy和Flask-Migrate集成到我们的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Previous imports remain... from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] ="postgresql://postgres:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d1a1bea2a5b6a3b4a291bdbeb2b0bdb9bea2a5">[emailprotected]</a>:5432/cars_api" db = SQLAlchemy(app) migrate = Migrate(app, db) class CarsModel(db.Model): __tablename__ = 'cars' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String()) model = db.Column(db.String()) doors = db.Column(db.Integer()) def __init__(self, name, model, doors): self.name = name self.model = model self.doors = doors def __repr__(self): return f"<Car {self.name}>" |
导入
然后,我们创建一个名为
我们通过在包含数据的列旁边使用
Flask带有命令行界面和专用命令。 例如,要启动我们的应用程序,我们使用命令
1 2 3 4 5 6 7 8 9 | $ export FLASK_APP=app.py $ flask run * Serving Flask app"app.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 172-503-577 |
放置好模型并集成
1 2 3 | $ flask db init $ flask db migrate $ flask db upgrade |
我们首先初始化数据库并启用迁移。 生成的迁移只是用于定义要在我们的数据库上执行的操作的脚本。 由于这是第一次,因此脚本将只生成带有模型中指定列的
如果我们添加,删除或更改任何列,我们总是可以执行
创建和读取实体
在数据库就位并连接到我们的应用程序之后,剩下的就是实现CRUD操作了。 让我们从创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Imports and CarsModel truncated @app.route('/cars', methods=['POST', 'GET']) def handle_cars(): if request.method == 'POST': if request.is_json: data = request.get_json() new_car = CarsModel(name=data['name'], model=data['model'], doors=data['doors']) db.session.add(new_car) db.session.commit() return {"message": f"car {new_car.name} has been created successfully."} else: return {"error":"The request payload is not in JSON format"} elif request.method == 'GET': cars = CarsModel.query.all() results = [ { "name": car.name, "model": car.model, "doors": car.doors } for car in cars] return {"count": len(results),"cars": results} |
我们首先定义一个同时接受
要创建新车,我们使用
为了将汽车保存到数据库中,我们通过
让我们尝试使用Postman之类的工具添加汽车:
响应消息通知我们我们的汽车已经创建并保存在数据库中:
您可以看到我们的数据库中现在有汽车的记录。
将汽车保存在我们的数据库中后,
这将返回
注意:请注意代码中没有单个SQL查询。 SQLAlchemy为我们解决了这一问题。
更新和删除实体
到目前为止,我们可以创建一辆汽车,并获取存储在数据库中的所有汽车的列表。 要在我们的API中完成对汽车的CRUD操作,我们需要添加功能以返回详细信息,修改和删除单个汽车。
我们将用来实现此目的的HTTP方法/动词是
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 | # Imports, Car Model, handle_cars() method all truncated @app.route('/cars/<car_id>', methods=['GET', 'PUT', 'DELETE']) def handle_car(car_id): car = CarsModel.query.get_or_404(car_id) if request.method == 'GET': response = { "name": car.name, "model": car.model, "doors": car.doors } return {"message":"success","car": response} elif request.method == 'PUT': data = request.get_json() car.name = data['name'] car.model = data['model'] car.doors = data['doors'] db.session.add(car) db.session.commit() return {"message": f"car {car.name} successfully updated"} elif request.method == 'DELETE': db.session.delete(car) db.session.commit() return {"message": f"Car {car.name} successfully deleted."} |
我们的方法
要更新我们的汽车的详细信息,我们使用
我们使用这些细节来修改我们的汽车对象,并使用
我们的车已成功更新。
最后,要删除汽车,我们向同一端点发送
结论
现实生活中的应用程序不像我们的应用程序那么简单,它们通常处理相关的数据并分布在多个表中。
SQLAlchemy允许我们定义关系并处理相关数据。 有关处理关系的更多信息,请参见官方的Flask-SQLAlchemy文档。
我们的应用程序可以轻松扩展以容纳关系和更多表。 我们还可以使用Binds连接到多个数据库。 有关绑定的更多信息,请参见"绑定"文档页面。
在这篇文章中,我们介绍了ORM,尤其是SQLAlchemy ORM。 使用Flask和Flask-SQLAlchemy,我们创建了一个简单的API,用于公开和处理存储在本地PostgreSQL数据库中的汽车数据。
这篇文章中该项目的源代码可以在GitHub上找到。