11.0 Databases and ORM
1. MVC architecture and how it maps to web development
- Purpose:Model-View-Controller (MVC) 是一种流行的服务器端 Web 应用程序架构设计模式。它描述了如何组织概念。
- Mapping to Web Development:
- Model:指应用程序中由数据库支持的 Python 对象(例如 SQLite)。一个 Model 是与数据库中的一个实体配对的对象。
- View:指由 Jinja 模板创建的 HTML 页面。
- Controller:指
routes.py
中的代码,负责连接 Model 和数据库,根据 Model 准备 View,以及更新和保存 Model 回数据库。
- Advantages:将 Web 应用程序划分为 MVC 具有多种优点:
- 可以修改 View 而不改变 Model。
- 可以替换数据库而不改变 View。
- 专业开发者可以分别处理各自的部分。
- 可以支持多个 View(例如,网页 + 移动应用)。
- 更容易隔离测试各部分。
- Designing:要设计 MVC 解决方案架构,需要确定所需的 Model、View 和 Controller。可以从用户故事中识别名词作为 Model,动词作为 routes(Controller 的一部分),并为指定用户关联 View。还可以模拟 View 的线框图(wireframe sketches),展示用户界面的基本布局和功能。也可以模拟典型的 HTTP 请求和响应。
2. Purpose, pros, cons of an ORM
- Purpose:Object-Relational Mapping (ORM) 库会自动创建对应于数据库表中每一行的对象,并允许通过原生代码(如 Python)直接操作数据库。它在数据库中的数据和应用程序中的 Model 之间建立链接。ORM 允许您通过对象与数据库交互,而无需编写实际的 SQL 查询。
- Pros:
- 加快开发时间,因为实现 Model 的类会自动生成相应的 SQL 代码。
- 抽象了数据库的具体实现——例如,可以在不改变代码的情况下从 SQLite 切换到 MySQL。
- 使与数据库的交互更加“Pythonic”和基于对象。
- 比编写原始 SQL 查询更容易。
- Cons:
- 有时性能会降低——库为您生成 SQL 查询,这些查询可能并非总是最优的。
- 可能难以理解您的代码正在执行的具体数据库操作。
- Usage:在课程项目组中,您将被要求使用 ORM 库。课程中使用 SQLAlchemy 作为 ORM。如果您在项目中使用纯 SQL 查询,将会扣分。
3. The very basics from your previous database course.
- Databases:是数据的有组织集合。需要将它们链接到 Web 应用程序以存储用户信息。
- Types:有关系型数据库 (relational databases)、文档数据库 (document databases)、图数据库 (graph databases) 等。Flask 可以支持多种数据库。关系型数据库是最流行和通用的方法。NoSQL 指“不仅仅是 SQL”,描述非关系型数据库。
- Relational Databases:将数据存储为一组关系 (relations),每个关系表示为一个表 (table)。表的每一行是一个实体 (entity),每一列是该实体的属性 (attribute)。每个关系都有一个对于该关系中每个实体都唯一的属性,称为 primary key。一些关系的属性是其他关系中的 primary key,这些属性称为 foreign keys。
- DBMS:Database Management System (DBMS) 是控制对数据库访问的应用程序。课程使用关系型数据库 SQLite 作为 DBMS,因为它设置简单、包体积小、将数据库存储为单个文件。缺点包括扩展性不佳、没有内置认证方法。
- Schema:数据库的 schema 是定义的表 (关系) 集合、属性类型以及属性约束。这是数据库的元数据 (meta-data),在应用程序的正常使用中不应改变。
- CRUD:Create, Read, Update, Delete (CRUD) 是任何持久化存储系统的基本操作。
- SQL:Sequential Query Language (SQL) 提供了执行 CRUD 操作的语法:Create 使用
insert
语句;Read 使用select
语句;Update 使用update
语句;Delete 使用delete
语句。
4. At a high-level how SQLAlchemy can be used to perform operations on the database via Python.
- SQLAlchemy 用于将 SQLite 数据库链接到 Flask 应用程序。
- 需要安装
flask-sqlalchemy
和flask-migrate
包。 - 链接步骤:配置 Flask 应用程序对象,创建一个名为
db
的 SQLAlchemy 对象,创建一个 migrate 对象,导入 models 模块。 - Adding Entities:
- 创建 Model 的实例。
- 使用
db.session.add()
将实例添加到db.session
对象。 - 注意,此时更改尚未保存到数据库,仅通知 DBMS 这些对象存在。
- 必须调用
db.session.commit()
才能将所有更改与数据库同步。这类似于git add
后再git commit
。 - Example:
- Basic Queries:
- 使用
.<model>.query
或session.query(<model>)
从数据库中提取实体。 .all()
:返回该类型的所有实体。.get(i)
:获取 primary key 为i
的 Model 实例。- Example:
- 使用
- Deleting Entities:
- 使用
db.session.delete()
删除 Model 实例。 - 必须调用
db.session.commit()
才能将更改反映到数据库。 - Example:
- 使用
- More Complicated Queries:可以使用链式查询语法执行复杂操作。
query.join()
:执行内连接 (inner joins)。query.outerjoin()
:执行左外连接 (left-outer-joins)。filter_by()
:过滤结果 (类似 SQL 的 WHERE 子句)。order_by()
:排序结果 (类似 SQL 的 ORDER BY 子句)。
5. The meaning of basic SQLAlchemy syntax for defining models
- Models 在
app/models.py
文件中定义。 - 导入
db
(SQLAlchemy 类的实例)。 - 定义继承自
db.Model
的类。每个这样的类对应数据库中的一个表。 - 使用
db.Column
定义列。- 第一个参数定义列的类型。有多种类型可用,包括 string, integer, text, datetime, float, boolean, pickle type, large binary 等。
- 可选的列参数包括
nullable
(是否允许为空),primary_key
(是否是主键),foreign_key
(是否是外键),unique
(值是否唯一),index
(是否创建索引) 等。
- Example Model Definition:
from app import db class Student(db.Model): student_id = db.Column(db.Integer, primary_key=True) # 整型主键 student_name = db.Column(db.String(50), nullable=False) # 字符串类型,最大长度50,不允许为空 group_id = db.Column(db.Integer, db.ForeignKey('group.group_id')) # 整型外键,引用 group 表的 group_id # relationship 定义在此处或 Group 模型中
- Flask-SQLAlchemy 可以从 Model 中自动提取数据库表的 schema。Model 类定义了数据库的 schema。
6. Purpose of SQLAlchemy relationships
- Purpose:Foreign key 允许访问链接对象的 ID,但需要运行单独的查询来查找对象。使用
db.relationship
可以直接将链接对象作为类的字段访问。这使得访问链接对象更加直观和“Pythonic”。 - Usage:在 Model 中使用
db.relationship
定义关系。 - Access:定义关系后,可以直接访问和分配链接对象,就像访问普通变量一样。例如,如果 Student Model 与 Group Model 有关系,可以通过
student_instance.group
直接获取关联的 Group 对象。 - Back-populates:
back-populates
参数链接双向关系。如果更新group.students
,那么每个相关student.group
字段也会更新。这确保了所有关联对象同时更新。 - Types:使用
db.relationship
方法可以创建一对一 (one-to-one)、一对多 (one-to-many) 和多对多 (many-to-many) 关系。 - Example Relationship Definition:
7. Purpose and high-level overview of how Database migrations work
- Purpose:数据库迁移 (Database migration) 是必要的,因为在应用程序开发过程中,Model 会频繁变更。它允许在不破坏现有数据的情况下更新数据库 schema。它就像数据库的版本控制,允许在不同版本之间前进或回退。这对于拥有大量用户或有价值数据的大型应用程序至关重要。
- How it works (High-level):
- 每个数据库更改都附带升级 (upgrade) 和降级 (downgrade) 脚本。这些脚本用于将数据库从一个版本移动到 Model 的前一个版本。
- SQLAlchemy 使用 Alembic 迁移框架,由
flask-migrate
包包装。 - Initialization:运行
flask db init
命令初始化数据库并创建迁移文件夹。 - Creating Migrations:
flask db revision
:手动创建迁移脚本。flask db migrate
:自动创建迁移脚本。它将更新的 Model 类与数据库的当前状态进行比较。生成的脚本包含upgrade()
和downgrade()
方法。
- Applying Migrations:
flask db upgrade
应用脚本使数据库与 Model 同步。 - Rolling Back:
flask db downgrade
回滚更改到上一个版本。
- Importance:确保数据库 schema 和 Model 保持同步。在项目组中,需要执行至少两次数据库迁移才能获得最高分。
- Version Control:迁移脚本应该上传到 Git 仓库。它们跟踪 schema 的变化,帮助团队成员同步。数据库文件本身 (
app.db
) 不应提交到 Git 仓库。
8. Best practice for storing images
- Direct Storage:可以使用 Blob 列类型将图像直接存储在数据库中。但这性能很差,因为数据库并非设计用于存储此类数据。
- Small-scale Projects:将图像存储在服务器的文件系统中,并在数据库中存储指向该文件的路径 (path)。这是一种更好的方法。
- Large-scale Projects:最好将图像存储在服务器之外的专用硬件上。这些硬件针对重度 IO 操作进行了优化,而服务器硬件并未优化。例如,将图像存储在 Amazon bucket 等外部服务上。
9. Considerations when building a real-life webserver
- 当应用程序达到一定规模时,数据库必须支持更多功能。
- Indexing:为频繁使用的数据添加索引 (indexes),以提高查询性能。
- Data Layout Optimisation:根据数据是经常读取还是写入来优化数据布局。
- Caching:缓存结果以避免重复执行相同的查询。对于多个用户频繁请求相同或最新数据时尤其有用。
- Security:数据库安全性至关重要。即使服务器被攻破,也要阻止恶意用户或应用程序访问数据库。需要在服务器上实施安全机制。
- Backups:执行备份以避免服务器故障时丢失数据库。对于大型项目非常重要。
- Auditing:审计 (auditing) 以跟踪数据库何时被谁更改。这对于管理数据的“黄金副本”和了解谁进行了哪些更改非常重要。