ホームページ >バックエンド開発 >Python チュートリアル >Flask-SQLAlchemy を使用して Python の Flask フレームワークでデータベースを管理するためのチュートリアル
Flask-SQLAlchemy を使用してデータベースを管理する
Flask-SQLAlchemy は、Flask アプリケーションでの SQLAlchemy の使用を簡素化する Flask 拡張機能です。 SQLAlchemy は、複数のデータベース バックエンドをサポートする強力なリレーショナル データベース フレームワークです。基盤となるデータベース アクセスに高度な ORM およびローカル SQL 機能を提供します。
他の拡張機能と同様に、pip 経由で Flask-SQLAlchemy をインストールします:
(venv) $ pip install flask-sqlalchemy
Flask-SQLAlchemy では、データベースは URL として指定されます。この表には、最も一般的な 3 つのデータベース エンジン URL 形式がリストされています:
これらの URL では、ホスト名は MySQL サービスをホストするサーバーを指し、ローカル (localhost) またはリモート サーバーの場合があります。データベース サーバーは複数のデータベースをホストできるため、database は使用するデータベース名を示します。データベースには認証が必要です。ユーザー名とパスワードはデータベース ユーザーの資格情報です。
注: > SQLite データベースにはサービスがないため、ホスト名、ユーザー名、およびパスワードをデフォルトにすることができ、データベースはディスク ファイル名になります。
アプリケーション データベース URL は、Flask 構成オブジェクトの SQLALCHEMY_DATABASE_URI キーで構成する必要があります。もう 1 つの便利なオプションは SQLALCHEMY_COMMIT_ON_TEARDOWN です。これを True に設定すると、リクエストごとにデータベース変更の自動コミットが有効になります。追加の構成オプションについては、Flask-SQLAlchemy のドキュメントを確認してください。
from flask.ext.sqlalchemy import SQLAlchemy basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] =\ 'sqlite:///' + os.path.join(basedir, 'data.sqlite') app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True db = SQLAlchemy(app)
SQLAlchemy によってインスタンス化された db オブジェクトはデータベースを表し、Flask-SQLAlchemy のすべての関数へのアクセスを提供します。
モデル定義
モデルは、アプリケーションによって使用される永続的なエンティティを指します。 ORM のコンテキストでは、モデルは通常、データベース テーブルの列に一致するプロパティを持つ Python クラスです。 Flask-SQLAlchemy データベース インスタンスは、基本クラスと、その構造を定義するための一連の補助クラスと関数を提供します。
class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) def __repr__(self): return '<Role %r>' % self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) def __repr__(self): return '<User %r>' % self.username
__tablename__ クラス変数は、データベース内のテーブルの名前を定義します。 Flask-SQLAlchemy は、__tablename__ が使用されている場合にデフォルトのテーブル名を指定しますが、これらのデフォルト名は複数形の命名規則に従っていないため、テーブルに明示的に名前を付けることが最善です。残りの変数はモデルのプロパティであり、db.Column クラスのインスタンスとして定義されます。
db.Column コンストラクターに渡される最初のパラメーターは、データベース列の型、つまりモデル属性のデータ型です。表 5-2 に、使用可能な列タイプの一部と、モデルで使用される Python タイプを示します。
最も一般的な SQLAlchemy 列タイプ
db.Column の残りのパラメーターは、各プロパティの構成オプションを指定します。
最も一般的な SQLAlchemy 列オプション
注: Flask-SQLAlchemy では、すべてのモデルの主キー列 (通常は id という名前) を定義する必要があります。
どちらのモデルにも、人が読める文字列を表示するための repr() メソッドが含まれています。これは完全に必要というわけではありませんが、デバッグやテストには便利です。
関係
リレーショナル データベースは、リレーションシップを使用して、異なるテーブル間の接続を確立します。関係図は、ユーザーとユーザーの役割の間の単純な関係を表します。ロールは複数のユーザーに属することができ、ユーザーは 1 つのロールしか持てないため、このロールとユーザーには 1 対多の関係があります。
次のモデル クラスは、 で表される 1 対多の関係を示しています。
class Role(db.Model): # ... users = db.relationship('User', backref='role') class User(db.Model): # ... role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
リレーションシップは、外部キーを使用して 2 つの行を接続します。 Userモデルに追加したrole_id列を外部キーとして定義し、リレーションシップを確立します。 db.ForeignKey() のパラメータroles.idで指定された列は、rolesテーブルの行のid値を保持する列として理解されたい。
ロール モデルに追加された users 属性は、関係のオブジェクト指向のビューを表します。 Role クラスのインスタンスを指定すると、users プロパティはそのロールに接続されているユーザーのセットを返します。 db.relationship() に指定された最初のパラメーターは、モデル内の関係の反対側を示します。クラスが定義されていない場合、このモデルは文字列として提供できます。
注: 以前、segmentdefault で問題が発生したため、SQLAlchemy のソース コードをざっと読みました。 ForeignKey クラスのカラムは 3 種類のパラメータを受け取ります。1 つは「モデル名.属性名」、もう 1 つは「テーブル名.カラム名」です。次はそれを使用してみます。時間。
db.relationship() の backref パラメーターは、User モデルに role 属性を追加することで逆の関係を定義します。この属性を使用すると、外部キーではなくオブジェクトとして、role_id の代わりにロール モデルにアクセスできます。
ほとんどの場合、 db.relationship() は独自の外部キー関係を見つけることができますが、どの列が外部キーとして使用されているかを判断できない場合があります。たとえば、User モデルに、Role への外部キーとして定義された列が 2 つ以上ある場合、SQLAlchemy は 2 つの列のうちどれを使用すればよいのかわかりません。外部キーの設定があいまいな場合は、追加パラメータ db.relationship() を使用する必要があります。添え字には、関係を定義するために使用されるいくつかの一般的な構成オプションがリストされています:
常用SQLAlchemy关系选项
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 5a来切换到这个版本的应用程序。
除了一对多关系还有其他种类关系。一对一关系可以表述为前面描述的一对多关系,只要将db.relationship()中的uselist选项设置为False,“多”就变为“一”了。多对一关系也可表示为将表反转后的一对多关系,或表示为外键和db.relationship()定义在“多”那边。最复杂的关系类型,多对多,需要一个被称作关联表的额外表。你将在第十二章学习多对多关系。
数据库操作
学习怎样使用模型的最好方式就是使用Python shell。以下部分将介绍最常见的数据库操作。
创建表
首先要做的第一件事情就是指示Flask-SQLAlchemy基于模型类创建数据库。db.create_all()函数会完成这些:
(venv) $ python hello.py shell >>> from hello import db >>> db.create_all()
如果你检查应用程序目录,你会发现名为data.sqlite的新文件,SQLite数据库名在配置中给出。如果数据库已存在db.create_all()函数不会重新创建或更新数据库表。这会非常的不方便当模型被修改且更改需要应用到现有的数据库时。更新现有的数据库表的蛮力解决方案是先删除旧的表:
>>> db.drop_all() >>> db.create_all()
不幸的是,这种方法有个不受欢迎的副作用就是摧毁旧的数据库中的所有数据。更新数据库问题的解决方案会在这章快结束的时候介绍。
插入行
下面的示例会创建新的角色和用户:
>>> from hello import Role, User >>> admin_role = Role(name='Admin') >>> mod_role = Role(name='Moderator') >>> user_role = Role(name='User') >>> user_john = User(username='john', role=admin_role) >>> user_susan = User(username='susan', role=user_role) >>> user_david = User(username='david', role=user_role)
模型的构造函数接受模型属性的初始值作为关键字参数。注意,甚至可以使用role属性,即使它不是一个真正的数据库列,而是一对多关系的高级表示。这些新对象的id属性没有显式设置:主键由Flask-SQLAlchemy来管理。到目前为止对象只存于Python中,他们还没有被写入数据库。因为他们的id值尚未分配:
>>> print(admin_role.id) None >>> print(mod_role.id) None >>> print(user_role.id) None
修改数据库的操作由Flask-SQLAlchemy提供的db.session数据库会话来管理。准备写入到数据库中的对象必须添加到会话中:
>>> db.session.add(admin_role) >>> db.session.add(mod_role) >>> db.session.add(user_role) >>> db.session.add(user_john) >>> db.session.add(user_susan) >>> db.session.add(user_david)
或,更简洁的:
>>> db.session.add_all([admin_role, mod_role, user_role, ... user_john, user_susan, user_david])为了写对象到数据库,需要通过它的commit()方法来提交会话:
>>> db.session.commit()
再次检查id属性;这个时候它们都已经被设置好了:
>>> print(admin_role.id) 1 >>> print(mod_role.id) 2 >>> print(user_role.id) 3
注:db.session数据库会话和第四章讨论的Flask会话没有任何联系。数据库会话也叫事务。
数据库会话在数据库一致性上是非常有用的。提交操作会原子性地将所有添加到会话中的对象写入数据库。如果在写入的过程发生错误,会将整个会话丢弃。如果你总是在一个会话提交相关修改,你必须保证避免因部分更新导致的数据库不一致的情况。
注:数据库会话也可以回滚。如果调用db.session.rollback(),任何添加到数据库会话中的对象都会恢复到它们曾经在数据库中的状态。
修改行
数据库会话中的add()方法同样可以用于更新模型。继续在同一shell会话中,下面的示例重命名“Admin”角色为“Administrator”:
>>> admin_role.name = 'Administrator' >>> db.session.add(admin_role) >>> db.session.commit()
注意:不过貌似我们在做更新操作的时候都不使用db.session.add(),而是直接使用db.session.commit()来提交事务。
删除行
数据库会话同样有delete()方法。下面的示例从数据库中删除“Moderator”角色:
>>> db.session.delete(mod_role) >>> db.session.commit()
注意删除,和插入更新一样,都是在数据库会话提交后执行。
返回行
Flask-SQLAlchemy为每个模型类创建一个query对象。最基本的查询模型是返回对应的表的全部内容:
>>> Role.query.all() [<Role u'Administrator'>, <Role u'User'>] >>> User.query.all() [<User u'john'>, <User u'susan'>, <User u'david'>]
使用过滤器可以配置查询对象去执行更具体的数据库搜索。下面的例子查找所有被分配“User”角色的用户:
>>> User.query.filter_by(role=user_role).all() [<User u'susan'>, <User u'david'>]
对于给定的查询还可以检查SQLAlchemy生成的原生SQL查询,并将查询对象转换为一个字符串:
>>> str(User.query.filter_by(role=user_role)) 'SELECT users.id AS users_id, users.username AS users_username, users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'
如果你退出shell会话,在前面的示例中创建的对象将不能作为Python对象而存在,但可继续作为行记录存在各自的数据库表中。如果你开始一个全新的shell会话,你必须从它们的数据库行中重新创建Python对象。下面的示例执行查询来加载名字为“User”的用户角色。
>>> user_role = Role.query.filter_by(name='User').first()
过滤器如filter_by()通过query对象来调用,且返回经过提炼后的query。多个过滤器可以依次调用直到需要的查询配置结束为止。
下面展示一些查询中常用的过滤器。
在需要的过滤器已经全部运用于query后,调用all()会触发query执行并返回一组结果,但是除了all()以外还有其他方式可以触发执行。常用SQLAlchemy查询执行器:
关系的原理类似于查询。下面的示例从两边查询角色和用户之间的一对多关系:
>>> users = user_role.users >>> users [<User u'susan'>, <User u'david'>] >>> users[0].role <Role u'User'>
此处的user_role.users查询有点小问题。当user_role.users表达式在内部调用all()时通过隐式查询执行来返回用户的列表。因为查询对象是隐藏的,是不可能通过附加查询过滤器进一步提取出来。在这个特定的例子中,它可能是用于按字母排列顺序返回用户列表。在下面的示例中,被lazy = 'dynamic'参数修改过的关系配置的查询是不会自动执行的。
app/models.py:动态关系
class Role(db.Model): # ... users = db.relationship('User', backref='role', lazy='dynamic') # ...
用这种方式配置关系,user_roles.user查询还没有执行,所以可以给它增加过滤器:
>>> user_role.users.order_by(User.username).all() [<User u'david'>, <User u'susan'>] >>> user_role.users.count() 2