一定のルールに従って保存されたデータベースのアプリケーションデータは、アプリケーションがクエリを送信し、必要なデータを取得します。リレーショナルモデルデータベースに基づいて、最も一般的に使用されるWebアプリケーション。彼らは構造化照会言語SQLを使用しているため、このデータベースはまた、SQLデータベースとして知られています。しかし、近年では、文書データベースおよびキー - 値データベースは、まとめのNoSQLデータベースとして知られているこれら二つのデータベースへの一般的な代替となっています
SQLデータベース
リレーショナル・データベースは、テーブル内の別のソリッドモデリングを適用するためのテーブルデータを格納します。
テーブル内の列の数は、行の数は可変であり、固定されています。列定義テーブルによって表されるデータの属性エンティティ。テーブル定義の一部の行または実際のデータに対応するすべての列。
テーブルは、テーブルの各行の一意な識別子であるプライマリーキーと呼ばれる特殊な列を有しています。テーブルはまた、テーブルまたは異なる行に主キーテーブルを参照し、外部キー列と呼ぶことができます。関係と呼ばれるラインとの間のこのリンクは、リレーショナルデータベースモデルの基盤です。
NoSQLデータベース
NosSQLデータベースは、一般的に代わりに文書レコードを使用するのでは、テーブルのコレクションの代わりに使用されます。カップリングに使用されるのNoSQLデータベース設計のアプローチが困難となり、そのほとんどがリンクをサポートしていません。
テーブルの数を減らすには、重複データの量を増加させます。データの重複は、クエリの速度を向上させることができます。
良好な効率的かつコンパクトな格納されたコンフィギュレーションデータ、ACIDパラダイムを使用して、リレーショナルデータベース、及び原子性、一貫性、分離、永続性を持つSQLデータベース。
NoSQLのデータベースは、パフォーマンス上の利点を得るために、ACID要件を緩和しました。
Pythonのデータベースフレームワークの機能:使いやすさ、パフォーマンス、ポータビリティ、フラスコ統合
使用フラスコSQLAlchemyのデータベース管理
フラスコアプリケーションでSQLAlchemyのを使用してフラスコSQLAlchemyの簡素化動作は、また、ネイティブSQLデータベースの低レベルの機能の使用を提供し、両方の高レベルのORMをサポートします。
フラスコ-SQLAlchemyの中で指定されたURLを使用したデータベース。
MySQLの
|
mysqlの://ユーザー名:パスワード@ホスト名/データベース
|
Postgresの |
PostgreSQLの://ユーザー名:パスワード@ホスト名/データベース
|
SQLiteの(Linuxの等)
|
SQLiteの:///絶対/パス/に/データベース
|
SQLiteの(勝利)
|
SQLiteの:/// C:/絶対/パス/に/データベース
|
これらのURLは、ホスト名がホストデータベースサービスを示し、ローカルホスト(ローカルホスト)、またはリモートサーバであってもよいです。あなたは、データ上の複数のデータベースサーバーをホストするため、データベースを使用して、データベースの名前を表すことができます。データベースの身元を確認する必要がある場合は、ユーザー名とパスワードのデータベースを使用すると、ユーザーの資格情報を提供します。
flask_sqlalchemy 輸入SQLAlchemyの からフラスコ輸入フラスコ アプリ =フラスコ(__name__ ) app.configを[ " SQLALCHEMY_DATABASE_URI " ] = ' mysqlの://ユーザー名:パスワード@ホスト名/データベース' #自動追跡、同期およびモデルデータベースの変更を変更し、Falseに提案されたセット、メモリ消費量を減らす App.configファイルを[SQLALCHEMY_TRACK_MODIFICATIONS] = 偽 #DBを使用して、データベース・アプリケーションを表すクラスSQLAlchemyの一例である DB = SQLAlchemyの(アプリ)
定義モデル
この用語は、アプリケーションの永続エンティティによって使用されているモデルを指します。ORMでは、モデルはPythonのクラスに一般的に、クラスは、データベースの列に対応する属性。
二つの共通の命名:データベースのテーブル名やイニシャル_ TBL_テーブル
クラスの役割(db.Model): __tablename__ = " tbl_role " ID = db.Column(db.Integer、PRIMARY_KEY = 真) 名前 = db.Column(db.String(32)、ユニーク= 真) ユーザーは = db.relationship(「ユーザー」、後方参照= 「役割」) #は、2つのテーブル間の関係を確立するために、実際のデータベースではありませんロールのユーザー、別のテーブルの後方参照逆推力装置、このテーブルを介してアクセスすることができます DEF __repr__ (自己): 「」「定義された後は、その表示オブジェクトが、より直感的に、カスタム表示することができたときに「」」 復帰 「ロールオブジェクト:名=%はS 」%をself.name クラスのユーザー(db.Model): "" " ユーザーテーブル" "" __tablename__ = " tbl_users " #指定されたデータベースのテーブル名 ID = db.Column(db.Integer、PRIMARY_KEY = TRUE) #テーブルのプライマリキーに指定され、主キー整数それは自動インクリメントの主キーに設定されている 名前= db.Column(db.String(64)、UNIQUE = TRUE) #データベースの作成、拘束長の分野における取引の種類を示し、繰り返されない パスワード= db.Column(db.String( 128 )) ROLE_ID = db.Column(db.Integer、db.ForeignKey(" tbl_role.id ")) #はバックステッピングのために、外部キーを指定しました
定義されていない場合は、データベーステーブル名に使用__Tablename__クラス変数の定義は、フラスコSQLAlchemyのは、モデルクラスに基づいてデフォルトの名前を生成しますが、デフォルトのテーブル名は、複数の命名規則の一般的な使用に準拠していません。残りの変数は、インスタンスまたはクラス関係db.Column関係オブジェクトとして定義されたモデルのクラス属性です。
最初のパラメータは、クラスのコンストラクタdb.Columnデータベースのカラム型とモデルの属性です。
最も一般的なタイプのSQLAlchemyのコラム:
フィールドタイプ名
|
タイプパイソン
|
説明
|
整数
|
int型
|
通常、整数、32ビットの一般的な
|
SmallInteger
|
int型
|
整数の小範囲、典型的には16
|
BigInteger
|
intまたはlong
|
整数精度を制限するものではありません。
|
浮く
|
浮く
|
フロート
|
数値
|
decimal.Decimal
|
通常、整数、32ビットの一般的な
|
弦
|
STR
|
可変長文字列
|
テキスト
|
STR
|
可変長文字列は、文字列は、以上の長さが最適化に限定されるものではなく、
|
ユニコード
|
ユニコード
|
可変長のUnicode文字列
|
Unicodeテキスト
|
ユニコード
|
可変長Unicode文字列、長い長さの文字列かを最適化
|
ブーリアン
|
BOOL
|
MySQLでTINYINT型で、ブール値
|
日付
|
datetime.date
|
日付
|
時間
|
datetime.timeの
|
時間
|
日付時刻
|
datetime.datetimeの
|
日時
|
間隔
|
datetime.timedelta
|
インターバル
|
列挙型
|
STR
|
文字列のセット
|
PickleType
|
任意のPythonオブジェクト
|
ピクルスは自動的にシリアライズ
|
LargeBinary
|
STR
|
バイナリブロブ
|
DECIMAL(P、D) |
MySQLでしばしば通貨の精度の正確さのために予約
|
残りのパラメータは、プロパティdb.Column設定オプションを指定します。
最も一般的に使用されるSQLAlchemyのオプション:
列オプション
|
説明
|
PRIMARY_KEY
|
もし主キー真、テーブルの代表
|
ユニーク
|
Trueの場合、この列の代表は、重複する値を許可していません。
|
指数
|
あなたがTrueにこの列のインデックスを作成する場合は、クエリの効率を向上させます
|
NULL可能
|
Trueの場合Flaseは、NULLを許可しない場合、ヌル値を許可します
|
デフォルト
|
この列定義のデフォルト値
|
外部キー制約の4種類
ForeignKeyのパラメータondelete
|
説明
|
RESTRICT
|
親テーブルのデータが削除されると、それはテーブルから削除することを拒否します
|
NO ACTIION
|
同上
|
カスケード
|
テーブルからの削除データ、削除データに親テーブルが続きます
|
SET NULL
|
親テーブルのデータがNULLの外部キーフィールドに設定されているテーブルから削除しました
|
関係
リンクテーブル内の異なる行の関係を使用してリレーショナルデータベース。
クラスの役割(db.Model): ... users = db.relationship("User", backref="role") class User(db.Model): ... role_id = db.Column(db.Integer, db.ForeignKey("tbl_role.id"))
关系使用usrs表中的外键连接两行。添加到User模型中的role_id列被定义为外键,传给db.Foreignkey的参数‘tbl_role.id’表名这列的值是tbl_role表中相应行的id。
db.relationship()的第一个参数表明这个关系的另一端是哪个模型。如果关联的模型类在其后定义,可使用字符串(模型类名)形式指定。backref参数向User模型中添加一个role属性,从而定义反向关系,通过User实例的这个role属性可以获取对应的Role模型对象,不用再通过外键查找。
多数情况下,db.relationship()都能自行找到关系中的外键,但有时却无法确定哪一列是外键,如两个列都定义为外键时,需要其他额外的参数。
常用的SQLAlchemy关系选项:
关系选项名
|
说明
|
backref
|
在关系的另一模型中添加反向引用
|
primaryjoin
|
明确指定两个模型之间使用的联结条件;只需在模棱两可的关系中指定
|
lazy
|
指定如何加载相关记录,可选值有select(首次访问时按需加载)、immediate(源对象加载后加载)、joined(加载记录,但使用联结)、subquery(立即加载,但使用子查询)、noload(永不加载)、dynamic(不加载记录,但提供加载记录的查询)
|
uselist
|
如果为Flase,不使用列表而使用标量值,常用于一对一关系
|
order_by
|
指定关系中记录的排序方式
|
secondary
|
指定多对多中记录的排序方式
|
secondaryjoin
|
在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件
|
除了一对多之外,还有其他类型。一对一关系可以用一对多关系表示,但调用db.relationship()时要把uselist设为False。多对一关系也可使用一对多表示,对调两个表即可,或者把外键和db.relationship()都放在多的这一侧。最复杂是多对多,需要用到第三张表,这个表称为关联表(联结表)。
数据库操作
创建表
db.create_all()函数将寻找所有db.Model的子类,然后在数据库中创建对应的表。如果数据库中表已经存在,那么db.create_all()不会重新创建或更新相应的表。如果修改模型后要把改动应用到现有的数据库,更新现有数据库表的蛮力方式是先删除旧表再重新创建(不可取)。 db.drop_all() # 清除数据库中所有数据 db.create_all() # 创建所有之前定义的模型类的表
插入行
role1 = Role(name="admin") # 创建Role对象,id是自增的不需要定义 us1 = User(name="wang", password="123", role_id=role1.id) us2 = User(name="zhou", password="456", role=role1) 模型的构造函数就受的参数是使用关键字参数指定的模型属性初始值。注User中role属性也可以使用,虽然它不是真正的数据库列,但却是一对多关系的高级表示。无需设定id属性,在多数数据库中主键由数据库自身管理。现在这些对象只存在于python中,还未写入数据库,所以id还未赋值。 对数据库的改动通过数据库会话管理,也称为事务。在Flask-SQLAlchemy中,会话由db.session表示。准备把对象写入数据库之前,要先将其添加到会话。 # 每次只添加一个 db.session.add(role1) db.session.add(us1) db.session.add(us2) # 每次添加多个 db.session.add_all([role1, us1, us2]) 添加会话后,再调用db.session.commit()方法提交会话。 db.session.commit() 数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误,那么整个会话都会失效。如果你始终把相关改动放在会话中提交,就能避免因部分更新导致的数据库不一致。 数据库会话也可以回滚,调用db.session.rollback()后,添加到数据库会话中的所有对象都将还原到它们在数据库在中的状态(提交新数据前)。 db.session.rollback()
修改数据行
在数据库会话上调用add()方法重新上传某属性,起到更新模型的作用。
us2.name = "zhao" db.session.add(us2) db.session.commit()
删除行
数据库会话还有delete()方法,起到从数据库删除的作用。会话提交后数据库才会执行
db.session.delete(us2)
db.session.commit()
查询行
每个模型类都有query对象。最基本的模型查询是使用all()方法取回对应表中的所有记录。
Role.query.all()
User.query.all()
使用过滤器可以配置query对象进行更精确的数据库查询。
若想查看SQLAlchemy为查询生成的原生SQL查询语句,只需把query对象转换成字符串,str(User.query.all())强制类型转换。
query对象常用的SQLAlchemy查询过滤器:
过滤器
|
说明
|
filter()
|
把过滤器添加到原查询上,返回一个新查询
|
filter_by()
|
把等值过滤器添加到原查询上,返回一个新查询
|
limit
|
指定数值限定原查询返回的结果数量,返回一个新查询
|
offset()
|
偏移原查询返回的结果,返回一个新查询
|
order_by()
|
根据指定条件对原查询结果进行排序,返回一个新查询
|
group_by()
|
根据指定条件对原查询结果进行分组,返回一个新查询
|
query对象最常用的SQLAlchemy查询执行方法:
执行器
|
说明
|
all()
|
以列表形式返回查询的所有结果
|
first()
|
返回查询的第一个结果,如果未查到,返回None
|
first_or_404()
|
返回查询的第一个结果,如果未查到,返回404错误响应
|
get()
|
返回指定主键对应的行,如不存在,返回None
|
get_or_404()
|
返回指定主键对应的行,如不存在,返回404错误响应
|
count()
|
返回查询结果的数量
|
paginate()
|
返回一个Paginate对象,它包含指定范围内的结果
|
one()
|
返回查询的第一个结果,如果未查到,抛出异常
|
one_or_none()
|
返回查询的第一个结果,如果未查到,返回None
|
scalar()
|
返回对应一行一列的查询结果
|
# 通过模型类的query对象 Role.query.all() # 查询多条对象,返回列表 ROle.query.first() # 查询第一个对象 Role.query.get("主键id值") # 查询对应主键值的对象 # 通过数据库会话的query对象 db.session.query(Role).all() # 查询多个实例对象,查询执行方法与上述方式相同 from flask_sqlalchemy import _or User.query.filter_by(name="wang").first().name # 获取通过过滤器后的查询对象的第一个值的name属性 User.query.filter(User.name=="wang", User.role_id==1).first() # 过滤器中逗号表并列关系 User.query.filter(or_(User.name=="wang", User.role_id==1)).first() # or_函数表或关系 User.query.filter(User.role_id.in_([1,2,3])) # in_函数表包含关系 User.query.filter(~User.role_id.in_([1,2,3])) # ~表反 User.query.filter(User.role_id.is_(1)) # is_函数表判断关系 User.query.filter(User.role_id.isnot()) # isnot函数表不为空关系 User.query.offset(2).limit(2).all() # offset跳过两条,从第三条开始,limit取两条 User.query.order_by("-id").all() # 按id排序 User.query.order_by(User.id.desc()).all() # 按id降序的方式排序 User.query.order_by(User.id.asc()).all() # 按id升序的方式排序,默认升序 #以查询内容进行分组,返回新的查询对象 db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all() Role.query.get(1).users # 关联查询 User.query.get(1).role # 通过backref对应的字符反向关联查询 users = role1.users # 隐式的查询会调用all()方法 users[0].role
role1.users隐式调用all()方法,而query是隐藏的,无法指定更精确的查询过滤器。可在定义关系对象relationship()中添加lazy='dynamic'参数,从而禁止自动执行查询。此时role1.users返回一个查询query对象,可手动添加过滤器再调用all()方法。
Flask-Migrate实现数据库迁移
在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库,最直接的方式就是删除旧表,但这样会丢失数据。更好的解决办法就是使用数据库迁移框架,它可以追踪数据库的变化,然后把变动以增量的方式应用到数据库中。
在flask中可以使用Flask-Migrate扩展,来实现数据迁移,并且集成到Flask-Script中,所有操作通过命令就能完成。
为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象
创建迁移仓库
from flask_migrate import Migrate # ... app = Flask(__name__) manager = Manager(app) # 创建脚本管理对象 db = SQLAlchemy(app) migrate = Migrate(app, db) # 在flask-script中添加一个db命令,与上面创建的db数据库对象无关 manager.add_command('db', MigrateCommand)
为了开放数据库迁移相关的命令,Flask-Migrate添加了flask db命令和几个子命令。在新项目中可以使用init子命令添加数据库迁移支持
flask db init # 创建migrations文件夹,所有迁移文件都放在里面
这个命令会创建migrations目录,所有迁移脚本都存放在这里,如果是通过git checkout检出示例项目的,那无须做这一步,因为GitHub仓库中已有迁移仓库。数据库迁移仓库中的文件要和应用的其他文件一起纳入版本控制。
创建迁移脚本
在Alembic中,数据库迁移用迁移脚本表示,脚本中有两个函数,分别是upgrade()和downgrade()。upgrade()函数把迁移中的改动应用到数据库中,downgrade()函数则将改动删除。Alembic具有添加和删除改动的能力,意味着数据库可重设到修改历史的任意一点。
我们可以使用revision命令手动创建Alembic迁移,也可使用migrate命令自动创建。手动创建的迁移只是一个骨架,upgrade()和downgrade()函数都是空的,开发者要使用Alembic提供的Operations对象指令实现具体操作。自动创建的迁移会根据模型定义和数据库当前状态的差异尝试生成upgrade()和downgrade()函数内容。
自动创建的迁移不一定总是正确,如我们重命名了一列,自动生成的迁移可能会把这当作删除的一列,然后又新增了一列。如果原封不动地使用自动生成的迁移,这一列中的数据就会丢失。
使用Flask-Migrate管理数据库模式变化的步骤如下:
1.对模型类做必要的修改
2.执行flask db migrate 命令,自动创建一个迁移脚本
3.检查自动生成的脚本,根据对模型的实际改动进行调整
4.把迁移脚本纳入版本控制
5.执行flask db upgrade 命令,把迁移应用到数据库中
flask db migrate -m "更改备注信息" # 生成迁移文件
如果一直使用git checkout 命令检出示例应用,那么无须执行migrate命令
更新数据库
检查并修正好迁移脚本后,执行flask db upgrade命令,把迁移应用到数据库中。
flask db upgrade # 上传更改,前进
对第一个迁移来说,其作用与调用db.create_all()方法一样,但在后续的迁移中,flask db upgrade 命令能把改动应用到数据库,且不影响其中保存的数据。
若已执行过db.create_all(),创建了数据库文件,此时flask db upgrade命令失败,可使用flask db stamp 命令把现有数据库标记为已更新。
添加几个迁移
在开发项目的过程中,时常要修改数据库模型。如果使用迁移框架管理数据库,必须在迁移脚本中定义所有改动,否则改动将不可复现。修改数据库的步骤与创建第一个迁移类似。
1.对数据库模型做必要修改
2.执行flask db migrate 命令,生成迁移脚本
3.检查自动生成的脚本,改正不准确的地方
4.执行flask db upgrade 命令,把改动应用到数据库
实现一个功能,可能要多次修改数据库模型才能得到预期结果。如果前一个脚本还未提交到源码控制系统中,可以继续在那个迁移中修改,以免创建大量无意义的小迁移脚本。
在前一个迁移脚本的基础上修改步骤如下:
1.执行flask db downgrade 命令,还原前一个脚本对数据库的改动(可能导致部分数据丢失)
2.删除前一个迁移脚本
3.执行flask db migrate 命令生成一个新的数据库迁移脚本。这个迁移脚本除了前面删除的那个脚本中的改动之外,还包括这一次对模型的改动
4.根据前面的说明,检查并应用迁移脚本
flask db downgrade 版本库 # 删除更改,回退,若无版本号默认回退一步 flask xxx.py db history # 查看历史记录