flask 数据库迁移使用的哪个拓展比较好

Flask 教程,第四部分:数据库 - 技术翻译 - 开源中国社区
当前访客身份:游客 [
已有文章 2348 篇
当前位置:
Flask 教程,第四部分:数据库
英文原文:
0人收藏此文章,
推荐于 4年前 (共 30 段, 翻译完成于 01-14) ()
参与翻译(4人):
这是我的在python中使用the
微框架开发web程序系列的第四篇文章。本系列教程的目标是开发一个全面的围脖,姑且叫微薄吧。
这是系列文章的目录
&翻译的不错哦!
在上一章中,我们创建了一个登录表单,并且完成了数据提交和验证的工作。
在这一章里,我们将要创建一个可以记录我们网站用户的数据库。
学习这章你需要有上一章的 微博(microblog) 程序,并且确保它能够正常的运行。&
&翻译的不错哦!
命令行方式运行Python脚本
在这个章节中,我们将写一些简单的数据库管理脚本。在此之前让我们来复习一下如何通过命令行方式执行Python脚本.
如果Linux 或者OS X的操作系统,需要有执行脚本的权限。例如:
chmod a+x script.py
该脚本有个指向使用解释器的命令行。再脚本赋予执行权限后就可以通过命令行执行,就像这样: like this:
./script.py &arguments&
然而,在Windows系统上这样做是不行的,你必须提供Python解释器作为必选参数,如:
flask/Scripts/python script.py &arguments&
为了避免Python解释器路径输入出错,你可以将你的文件夹microoblog/flask/Scripts添加到系统路径,确保能正常显示Python解释器。
从现在开始,在Linux/OS X上的语句简洁。如果你使用Windows系统请记得转换语句。&
&翻译的不错哦!
在Flask使用数据库
我们将使用 的扩展来管理数据库。由项目提供的,已封装了关系对象映射(ORM)的一个插件。
ORMs允许数据库程序用对象的方式替代表和SQL语句。面向对象的操作被ORM转化为数据库命令。这样就意味着,不用sql语句,让Flask-SQLAlchemy为我们执行sql语句。
&翻译的不错哦!
大多数数据库教程都覆盖了创建和使用一个数据库的方法,但是没有充分解决当应用程序扩展时数据库更新的问题。通常,你会删除旧的数据库,然后再创建一个新的数据库来达到更新的效果,这样就丢失了所有的数据。如果这些数据创建起来很费劲,那么我们不得不写导入导出的脚本了。
幸运的是,我们有了更好的方案.
我们现在可以使用做数据库迁移的更新了,虽然它增加了数据库启动时的负担,但这点小小的代价还是值得的,毕竟我们不用担心手动迁移数据库的问题了。
理论学习完毕,我们开始吧!
&翻译的不错哦!
我们的小程序使用sqlite数据库。sqlite是小程序数据库的最佳选择,一个可以以单文件存储的数据库。
在我们的配置文件中添加新的配置项 (fileconfig.py):
basedir = os.path.abspath(os.path.dirname(__file__))
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_DATABASE_URI是the Flask-SQLAlchemy必需的扩展。这是我们的数据库文件的路径。
SQLALCHEMY_MIGRATE_REPO 是用来存储SQLAlchemy-migrate数据库文件的文件夹。
&翻译的不错哦!
最后,初始化应用的时候也需要初始化数据库。这里是升级后的init文件(fileapp/__init):
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
from app import views, models
注意生成的脚本已改动2个地方。我们现在开始创建数据库的adb对象,引用新的模块。马上来写这个模块。
&翻译的不错哦!
数据库模型
我们在数据库存储的数据通过数据库model层被映射为一些类里面的对象,ORM层将根据类对象映射到数据库对应的字段.
让我们来创建个映射到users的model。使用工具,我们创建了代表users表的一个图标:
&翻译的不错哦!
id字段通常作为主键的形式用在所有的models里面,每个在数据库中的user都有一个指定的唯一id值。幸运的是,这些都是自动的,我们只需要提供一个id字段。
nickname和email字段被定义为string类型,他们的长度也已经被指定,这样可以节省数据库存储空间。
role字段被定义为integer类型,我们用来标识users是admins还是其他类型。
&翻译的不错哦!
现在我们已经明确了users表的结构,接下来转换为编码的工作将相当简单了(fileapp/models.py):
from app import db
ROLE_USER = 0
ROLE_ADMIN = 1
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
nickname = db.Column(db.String(64), index = True, unique = True)
email = db.Column(db.String(120), index = True, unique = True)
role = db.Column(db.SmallInteger, default = ROLE_USER)
def __repr__(self):
return '&User %r&' % (self.nickname)
User类把我们刚刚创建的几个字段定义为类变量。字段使用db.Column类创建实例,字段的类型作为参数,另外还提供一些其他可选参数。例如,标识字段唯一性和索引的参数.
__repr__方法告诉Python如何打印class对象,方便我们调试使用。
&翻译的不错哦!
创建数据库
把配置和model放到正确的目录位置,现在我们创建数据库文件。SQLAlchemy-migrate包自带命令行工具和APIs来创建数据库,这样的方式可以方便以后更新。但是我觉得使用这个命令行工具有些别扭,所以我自己写了个python脚本来调用迁移的APIs.
&翻译的不错哦!
这里有个创建数据库的脚本&(filedb_create.py):
#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
from app import db
import os.path
db.create_all()
if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))
注意这个脚本是完全通用的,所有的应用路径名都是从配置文件读取的。当你用在自己的项目时,你可以把脚本拷贝到你app`s目录下就能正常使用了。
&翻译的不错哦!
创建数据库你只需要运行下面的一条命令(注意windows下稍微有些不同):
./db_create.py
运行这条命令之后,你就创建了一个新的app.db文件。这是个支持迁移的空sqlite数据库,同时也会生成一个带有几个文件的db_repository目录,这是SQLAlchemy-migrate存储数据库文件的地方,注意如果数据库已存在它就不会再重新生成了。这将帮助我们在丢失了现有的数据库后,再次自动创建出来。.
&翻译的不错哦!
第一次迁移
既然我们已经定义好了model,也把它和数据库做了关联,接下来我们来初次尝试下做一个改变应用数据库结构的一次迁移,这将帮助我们从一个空的数据库变成一个可以存储users信息的数据库。
做一个迁移我使用另一个Python小助手脚本&(filedb_migrate.py):
#!flask/bin/python
import imp
from migrate.versioning import api
from app import db
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1)
tmp_module = imp.new_module('old_model')
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
exec old_model in tmp_module.__dict__
script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata)
open(migration, "wt").write(script)
a = api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print 'New migration saved as ' + migration
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
这个脚本看起来很复杂,其实做的东西真不多。SQLAlchemy-migrate通过对比数据库的结构(从app.db文件读取)和models结构(从app/models.py文件读取)的方式来创建迁移任务,两者之间的差异将作为一个迁移脚本记录在迁移库中,迁移脚本知道如何应用或者撤销一次迁移,所以它可以方便的升级或者降级一个数据库的格式。
&翻译的不错哦!
虽然我使用上面的脚本自动生成迁移时没遇到什么问题,但有时候真的很难决定数据库旧格式和新格式究竟有啥改变。为了让SQLAlchemy-migrate更容易确定数据库的改变,我从来不给现有字段重命名,限制了添加删除models、字段,或者对现有字段的类型修改。我总是检查下生成的迁移脚本是否正确。
不用多讲,在你试图迁移数据库前必须做好备份,以防出现问题。不要在生产用的数据库上运行第一次使用的脚本,先在开发用的数据库上运行下。
&翻译的不错哦!
继续前进,记录下我们的迁移:
./db_migrate.py
脚本将打印出以下信息:
New migration saved as db_repository/versions/001_migration.py
Current database version: 1
这个脚本信息显示了迁移脚本的存放位置,还有当前数据库的版本号。空数据库的版本号是0,当我们导入users信息后版本号变为1.
&翻译的不错哦!
数据库的升级和回滚
现在你可能想知道为什么我们要做额外的工作来做数据库的迁移记录。
试想一下,你有个应用在开发机器上,同时服务器上也有一个复制的应用正在运行。
比方说,在你产品的下个版本你的models层作了修改,比如增加了一个新表。没有迁移文件的话,你需要同时解决在开发机和服务器上数据库格式修改的问题,这将是个很大的工作量。
&翻译的不错哦!
如果你已经有了一个支持迁移的数据库,那么当你向生产服务器发布新的应用版本时,你只需要记录下新的迁移记录,把迁移脚本拷贝到你的生产服务器上,然后运行一个简单的应用改变脚本就行。数据库的升级可以使用下面的Python脚本(filedb_upgrade.py):
#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
当你运行上面的脚本时,数据库将升级到最新版本,并通过脚本将改变信息存储到数据库中。
&翻译的不错哦!
把数据库回滚到旧的格式,这是不常见的一个方式,但以防万一,SQLAlchemy-migrate也很好的支持(filedb_downgrade.py):
#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1)
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
这个脚本将回滚数据库的一个版本,你可以通过运行多次的方式向前回滚多个版本。
&翻译的不错哦!
数据库关联
关系型数据库最擅长存储数据之间的关系。假如用户会写一篇微博,用户的信息被存储在users表中,微博存储在post表中。记录谁写的微博最有效的方式是建立两条数据之间的关联.
一旦用户和微博的关系表建立之后,我们有两种查询方式可以使用。.最琐碎的一个就是当你看到一篇微博,你想知道是哪个用户写的。更复杂的一个是反向的查询,如果你知道一个用户,你想了解下他写的全部微博。Flask-SQLAlchemy将给我们提供对两种方式查询的帮助。
&翻译的不错哦!
让我们对数据做一下扩展来存储微博信息,这样我们就能看到对应的关系了。我们回到我们使用的数据库设计工具来创建个posts表:
posts表包含一个必须的id,微博的内容body,还有一个时间戳。没有什么新东西,但是user_id字段值得解释下。
我们想建立用户和他们写的微博之间的关联,这种方法就是通过添加一个包含用户id的字段来标识谁写的微博,这个id叫做外键。我们的数据库设计工具也显示了外键作为一个外键和id字段指向表的连接。这种关联叫做一对多关联,也就是一个用户可以写多篇文章。
&翻译的不错哦!
让我们修改下models来响应这些变化&(app/models.py):
from app import db
ROLE_USER = 0
ROLE_ADMIN = 1
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
nickname = db.Column(db.String(64), unique = True)
email = db.Column(db.String(120), unique = True)
role = db.Column(db.SmallInteger, default = ROLE_USER)
posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')
def __repr__(self):
return '&User %r&' % (self.nickname)
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '&Post %r&' % (self.body)
我们增加了一个表示用户写的微博的Post类,user_id字段在Post类中被初始化指定为一个外键,因此Flask-SQLAlchemy会知道这个字段将会和用户做关联。
&翻译的不错哦!
注意我们还在User类中添加了一个新字段命名为posts,它被定义成一个db.relationship字段,这个字段并非是数据库中实际存在的字段,所以它不在我们的数据库图表中。对于一对多的关联db.relationship字段通常只需要在一边定义。根据这个关联我们可以获取到用户的微博列表。db.relationship的第一个参数表示“many”一方的类名。backref参数定义了一个字段将"many"类的对象指回到"one"对象,就我们而言,我们可以使用psot.author获取到User实例创建一个微博。如果理解不了不要担心,在文章的后面我们将通过一个例子来解释。
&翻译的不错哦!
让我们用另外一个迁移文件记录下这次的改变。简单运行下面脚本:
./db_migrate.py
运行脚本后将得到如下输出:
New migration saved as db_repository/versions/002_migration.py
Current database version: 2
我们没必要每次都用一个独立的迁移文件来记录数据库model层的小变化,一个迁移文件通常只是记录一个发布版本的改变。接下来更重要的事情是我们需要了解下迁移系统的工作原理。
&翻译的不错哦!
我们已经花了大量的时间在数据库定义上,但是我们仍然没有看到他是如何工作的,因为我们的应用程序里没有任何的数据相关的编码,接下来我们将在Python解释器里使用我们的崭新数据库吧。
继续前进,启动Python。&在 Linux&或者 OS X:
flask/bin/python
Windows下:
flask/Scripts/python
当你在Python命令行提示符中输入下面信息:
&&& from app import db, models &&&
这样我们的数据库模块和models就被加载到了内存里.
让我们来创建个新用户:
&&& u = models.User(nickname='john', email='', role=models.ROLE_USER)
&&& db.session.add(u)
&翻译的不错哦!
在同一个会话环境下更改数据库,多次的修改可以积累到一个会话中最后通过调用一个mit()命令提交,提交同时也保证了原子性。如果在会话中出现了错误,会调用db.session.rollback()把数据库回滚到会话之前的状态。如果调用的既不是提交也不是回滚,那么系统会默认回滚这个会话。Sessions(会话)保证了数据库的数据一致性。
&翻译的不错哦!
让我们来添加另外一个用户:
&&& u = models.User(nickname='susan', email='', role=models.ROLE_USER)
&&& db.session.add(u)
现在我们可以查询出用户信息:
&&& users = models.User.query.all()
&&& print users
[&User u'john'&, &User u'susan'&]
&&& for u in users:
print u.id,u.nickname
此处我们使用了query查询函数,在所有的model类中都可以使用这个函数。注意id是如何自动生成的。
还有另外一种方式来查询,如果我们知道了用户的id,我们可以使用下面的方式查找用户信息:
&&& u = models.User.query.get(1)
&&& print u
&User u'john'&
现在让我们添加一条微博信息:
&&& import datetime
&&& u = models.User.query.get(1)
&&& p = models.Post(body='my first post!', timestamp=datetime.datetime.utcnow(), author=u)
&&& db.session.add(p)
&翻译的不错哦!
这个地方我们把时间设置为UTC时区,所有的存储在数据库里的时间将是UTC格式,用户可能在世界各地写微博,因此我们需要使用统一的时间单位。在以后的教程中我们将学习如何在用户本地时区使用这些时间。
你也许注意到我们没有在Post类中设置user_id字段,取而代之的是把用户对象存储到了author字段。auhtor字段是个通过Flask-SQLAlchemy添加的虚拟字段用来建立关联关系的,我们之前已经定义好了这个名字,参照:model中的db.relationship中backref参数。通过这些信息,ORM层就能知道如何取到user_id。
&翻译的不错哦!
要完成这个会话,让我们来看看更多可做的数据库查询:
# get all posts from a user
&&& u = models.User.query.get(1)
&&& print u
&User u'john'&
&&& posts = u.posts.all()
&&& print posts
[&Post u'my first post!'&]
# obtain author of each post
&&& for p in posts:
print p.id,p.author.nickname,p.body
1 john my first post!
# a user that has no posts
&&& u = models.User.query.get(2)
&&& print u
&User u'susan'&
&&& print u.posts.all()
# get all users in reverse alphabetical order
&&& print models.User.query.order_by('nickname desc').all()
[&User u'susan'&, &User u'john'&]
要了解更多的数据库查询选项,最好的方式就是去看
在结束会话之前,我们把之前创建的测试用户和文章删除掉,就可以在接下来的章节,从一个干净的数据库开始:
&&& users = models.User.query.all()
&&& for u in users:
db.session.delete(u)
&&& posts = models.Post.query.all()
&&& for p in posts:
db.session.delete(p)
&翻译的不错哦!
这一长篇新手入门,我们了解到了数据库的基本操作,但我们还没有将数据库关联到程序中。在下一个章节中我们通过用户登录系统来练习所学的数据库操作。
在此同时,如果你还没开始写程序,你需要下载当前文件&.注意,zip文件中没有包括数据库,但是已经有存储脚本。用db_create.py创建一个新的数据库,用db_upgrade.py把你的数据库升级到最新版本。
&翻译的不错哦!
学习下Flask,顺便翻译下Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
作者:ipython
字体:[ ] 类型:转载 时间:
Flask-Migrate可以帮助Flask应用程序通过预设的Python脚本完成数据库迁移操作,这里我们就来看一下Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程,需要的朋友可以参考下
我们在升级系统的时候,经常碰到需要更新服务器端数据结构等操作,之前的方式是通过手工编写alter sql脚本处理,经常会发现遗漏,导致程序发布到服务器上后无法正常使用。
现在我们可以使用Flask-Migrate插件来解决之,Flask-Migrate插件是基于Alembic,Alembic是由大名鼎鼎的SQLAlchemy作者开发数据迁移工具。
具体操作如下:
1. 安装Flask-Migrate插件
$ pip install Flask-Migrate
2. 修改Flask App部分的代码,以增加Migrate相关的Command
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
$ python app.py db init
使用Flask-Migrate迁移数据库
随着开发进度不断向前,你会发现你的数据库模型需要更改,而当这种情况发生时需要更新数据库。
Flask-SQLAlchemy只有当数据库表不存在了才从模型创建它们,所以更新表的唯一途径就是销毁旧的表,当然这将导致所有数据库中的数据丢失。
有个更好的解决方案就是使用数据库迁移框架。和源码版本控制工具跟踪更改源码文件一样,数据库迁移框架跟踪更改数据库模型,然后将增量变化应用到数据库中。
SQLAlchemy的主要开发人员写了一个Alembic迁移框架,但我们不直接使用Alembic,Flask应用可以使用Flask-Migrate扩展,一个集成了Flask-Script来提供所有操作命令的轻量级Alembic包。
4. 创建迁移仓库
首先,Flask-Migrate必须已经安装到虚拟环境中:
(venv) $ pip install flask-migrate
下面展示扩展如何初始化:
from flask.ext.migrate import Migrate, MigrateCommand
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
为了可以使用数据库迁移命令,Flask-Migrate提供MigrateCommand类来连接Flask-Script的manager对象。在这个示例中使用db来连接到命令。
在数据库迁移可以维护之前,必须通过init子命令来创建一个迁移库:
(venv) $ python hello.py db init
Creating directory /home/flask/flasky/migrations...done
Creating directory /home/flask/flasky/migrations/versions...done
Generating /home/flask/flasky/migrations/alembic.ini...done
Generating /home/flask/flasky/migrations/env.py...done
Generating /home/flask/flasky/migrations/env.pyc...done
Generating /home/flask/flasky/migrations/README...done
Generating /home/flask/flasky/migrations/script.py.mako...done
Please edit configuration/connection/logging settings in
'/home/flask/flasky/migrations/alembic.ini' before proceeding.
这个命令创建一个migrations文件夹,里面存放了所有迁移脚本。
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 5c来切换到这个版本的应用程序。
5. 创建迁移脚本
在Alembic,数据库迁移工作由迁移脚本完成。这个脚本有两个函数,分别叫做upgrade()和downgrade()。upgrade()函数实施数据库更改,是迁移的一部分,downgrade()函数则删除它们。通过添加和删除数据库变化的能力,Alembic可以重新配置数据库从历史记录中的任何时间点。
Alembic迁移可以分别使用revision和migrate命令手动或自动创建。手动迁移通过由开发人员使用Alembic的Operations对象指令实现的空upgrade()和downgrade()函数创建迁移框架脚本。另一方面,自动迁移通过寻找模型定义和数据库当前状态间的不同为upgrade()和downgrade()生成代码。
警告:自动迁移并不总是准确的,可以忽略一些细节。所以应该经常审查一下自动生成的迁移脚本。
migrate子命令创建自动迁移脚本:
(venv) $ python hello.py db migrate -m "initial migration"
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate] Detected added table 'roles'
INFO [alembic.autogenerate] Detected added table 'users'
INFO [pare] Detected added index
'ix_users_username' on '['username']'
Generating /home/flask/flasky/migrations/versions/1bc
_initial_migration.py...done
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 5c来切换到这个版本的应用程序。注意,你不需要为这个应用生成migrations,所有的迁移脚本都包含在版本库中。
6. 更新数据库
一旦迁移脚本被审查且接受,就可以使用db upgrade命令更新到数据库中:
(venv) $ python hello.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.migration] Running upgrade None -& 1bc, initial migration
第一次迁移实际上相当于调用db.create_all(),但在后续迁移中,upgrade命令对表实施更新操作但不影响表中的内容。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具关系数据库基于关系,有主键、外键等。它存储数据高效,避免了重复;修改数据方便。缺点是连接的成本过高。
NoSQL尽量避免连接,但是增加了数据冗余,修改数据麻烦,不过查询快速。
对于中小型的应用,两者的效率差别不大。
python数据库框架
Flask对数据库没有限制,你可以使用MySQL, Postgres, SQLite, Redis, MongoDB或CouchDB等。另外还有数据抽象层SQLAlchemy和MongoEngine等。
选择数据库时需要关注:
易用性:抽象层,比如ORM(object-relational mapper)或者ODM(object-document mapper)通常更有优势。
性能:通常ORM或ODM因为需要多一次转换,性能更低,但是影响不是很大。
移植性:要注意不同数据库和云平台等的移植性。
Flask集成:使用Flask的扩展通常更好。
使用Flask-SQLAlchemy进行数据库管理
安装pip install flask-sqlalchemy。各种数据的链接形式如下:
Database engine
mysql://username:password@hostname/database
postgresql://username:password@hostname/database
SQLite (Unix)
sqlite:////absolute/path/to/database
SQLite (Windows)
sqlite:///c:/absolute/path/to/database
下面我们示例一下连接sqlite:
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_COMMIT_ON_TEARDOWN会在每次请求结束后自动提交数据库改变。
模型是应用程序使用的持久性实体。ORM下模型通常是Python类的属性和数据库表的列的对应。定义示例如下:
class&Role(db.Model):
&&&&__tablename__&=&'roles'
&&&&id&=&db.Column(db.Integer,&primary_key=True)
&&&&name&=&db.Column(db.String(64),&unique=True)
&&&&users&=&db.relationship('User',&backref='role',&lazy='dynamic')
&&&&def&__repr__(self):
&&&&&&&&return&'&Role&%r&'&%&self.nameclass&User(db.Model):
class&User(db.Model):
&&&&__tablename__&=&'users'
&&&&id&=&db.Column(db.Integer,&primary_key=True)
&&&&username&=&db.Column(db.String(64),&unique=True,&index=True)
&&&&role_id&=&db.Column(db.Integer,&db.ForeignKey('roles.id'))
&&&&def&__repr__(self):
&&&&&&&&return&'&User&%r&'&%&self.username
类变量 __tablename__ 定义在数据库中使用的表名。如果没有定义 __tablename__ ,Flask-SQLAlchemy会使用默认名字, 表名没有遵守使用复数形式进行命名的约定, 所以最好由我们自己来指定表名。
最常用的SQLAlchemy列类型如下:
Python type Description
Integerint Regular integer, typically 32 bits
SmallInteger
Short-range integer, typically 16 bits
BigInteger
int or long
Unlimited precision integer
Floating-point number
decimal.Decimal
Fixed-point number
Variable-length string
Variable-length string, optimized for large or unbound length
Variable-length Unicode string
UnicodeText
Variable-length Unicode string, optimized for large or unbound length
Boolean value
datetime.date
Date value
datetime.time
Time value
datetime.datetime
Date and time value
datetime.timedelta
Time interval
List of string values
PickleType
Any Python object
Automatic Pickle serialization
LargeBinary
Binary blob
虽然没有强制要求,但这两个模型都定义了 __repr()__ 方法,返回一个具有可读性的字符串表示模型,可在调试和测试时使用。
最常见的SQLAlchemy选项:
Option name
Description
primary_key
If set to True , the column is the table’s primary key.
If set to True , do not allow duplicate values for this column.
If set to True , create an index for this column, so that queries are more efficient.
If set to True , allow empty values for this column. If set to False , the column will not allow null values.
Define a default value for the column.
角色到用户的一对多关系:
class&Role(db.Model):
&&&&users&=&db.relationship('User',&backref='role')
class&User(db.Model):
&&&&role_id&=&db.Column(db.Integer,&db.ForeignKey('roles.id'))
"users = db.relationship('User', backref='role')“表示代表关系基于对象的视图。能返回指定角色的相关人员列表。db.relationship()中如果对方表还没有定义好,可以用字符串表示。backref定义了反向关系,给User模型添加了role属性,它可以对象的形式代替Role模型的外键role_id。当外键由多个组成时,还需要考虑一些其他参数:
Option name
Description
Add a back reference in the other model in the relationship.
primaryjoin
Specify the join condition between the two models explicitly. This is necessary only for ambiguous relationships.
If set to False , use a scalar instead of a list.
Specify the ordering used for the items in the relationship.
Specify the name of the association table to use in many-to-many relationships.
secondaryjoin
Specify the secondary join condition for many-to-many relationships when SQLAlchemy cannotdetermine it on its own.
数据库操作
$&&python&hello.py&shell
In&[1]:&from&hello&import&db
In&[2]:&db.create_all()In&[3]:&db.drop_all()In&[4]:&db.create_all()
第一次执行db.create_all()的时候会生成data.sqlite文件。
先插入一些角色和用户。
In&[1]:&from&hello&import&Role,&User
In&[2]:&admin_role&=&Role(name='Admin')In&[3]:&mod_role&=&Role(name='Moderator')In&[4]:&user_role&=&Role(name='User')In&[5]:&user_john&=&User(username='john',&role=admin_role)In&[6]:&user_susan&=&User(username='susan',&role=user_role)In&[7]:&user_david&=&User(username='david',&role=user_role)
现在上述角色和用户是没有写入数据库的:
In&[8]:&print(admin_role.id)NoneIn&[9]:&print(mod_role.id)NoneIn&[10]:&print(user_role.id)None
下面写入数据库:
In&[12]:&from&hello&import&db
In&[13]:&db.session.add_all([admin_role,&mod_role,&user_role,user_john,&user_susan,&user_david])In&[14]:&mit()In&[15]:&print(admin_role.id)1In&[16]:&print(mod_role.id)2In&[17]:&print(user_role.id)3
注意这里session表示数据库会话。另外也可以单条的形式写入:db.session.add(admin_role)。mit()会按事务形式操作,db.session.rollback()还支持回滚。
add()可用于更新行:
In&[18]:&admin_role.name&=&'Administrator'In&[19]:&db.session.add(admin_role)In&[20]:&mit()
add()可用于更新行:
In&[21]:&db.session.delete(mod_role)In&[22]:&mit()
In&[23]:&Role.query.all()Out[23]:&[&Role&u'Administrator'&,&&Role&u'User'&]n&[24]:&User.query.all()Out[24]:&[&User&u'john'&,&&User&u'susan'&,&&User&u'david'&]In&[25]:&User.query.filter_by(role=user_role).all()Out[25]:&[&User&u'susan'&,&&User&u'david'&]In&[26]:&str(User.query.filter_by(role=user_role))Out[26]:&实际的sql语句In&[27]:&user_role&=&Role.query.filter_by(name='User').first()In&[28]:&user_role
Out[28]:&&Role&u'User'&In&[29]:&users&=&user_role.users
In&[30]:&users
Out[30]:&&sqlalchemy.orm.dynamic.AppenderBaseQuery&at&0x7f&In&[31]:&users[0].role
Out[31]:&&Role&u'User'&In&[32]:&user_role.users.order_by(User.username).all()Out[32]:&[&User&u'david'&,&&User&u'susan'&]In&[33]:&user_role.users.count
Out[33]:&&bound&method&AppenderBaseQuery.count&of&&sqlalchemy.orm.dynamic.AppenderBaseQuery&object&at&0x7f&&
注意app/models.py中的定义:
users&=&db.relationship('User',&backref='role',&lazy='dynamic')
上面的dynamic是一定要指定的,要不然后面不能指定其他过滤条件,默认查询所有,负担比较重。
在视图函数中使用数据库
可以在hello.py的视图函数直接使用使用数据库,修改如下:
import&osfrom&flask&import&Flask,&render_template,&session,&redirect,&url_forfrom&flask.ext.script&import&Managerfrom&flask.ext.bootstrap&import&Bootstrapfrom&flask.ext.moment&import&Momentfrom&flask.ext.wtf&import&Formfrom&wtforms&import&StringField,&SubmitFieldfrom&wtforms.validators&import&Requiredfrom&flask.ext.sqlalchemy&import&SQLAlchemy
basedir&=&os.path.abspath(os.path.dirname(__file__))app&=&Flask(__name__)app.config['SECRET_KEY']&=&'hard&to&guess&string'app.config['SQLALCHEMY_DATABASE_URI']&=\&&&&'sqlite:///'&+&os.path.join(basedir,&'data.sqlite')app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']&=&Truemanager&=&Manager(app)bootstrap&=&Bootstrap(app)moment&=&Moment(app)db&=&SQLAlchemy(app)class&Role(db.Model):
&&&&__tablename__&=&'roles'
&&&&id&=&db.Column(db.Integer,&primary_key=True)
&&&&name&=&db.Column(db.String(64),&unique=True)
&&&&users&=&db.relationship('User',&backref='role',&lazy='dynamic')
&&&&def&__repr__(self):
&&&&&&&&return&'&Role&%r&'&%&self.nameclass&User(db.Model):
&&&&__tablename__&=&'users'
&&&&id&=&db.Column(db.Integer,&primary_key=True)
&&&&username&=&db.Column(db.String(64),&unique=True,&index=True)
&&&&role_id&=&db.Column(db.Integer,&db.ForeignKey('roles.id'))
&&&&def&__repr__(self):
&&&&&&&&return&'&User&%r&'&%&self.usernameclass&NameForm(Form):
&&&&name&=&StringField('What&is&your&name?',&validators=[Required()])
&&&&submit&=&SubmitField('Submit')@app.errorhandler(404)def&page_not_found(e):
&&&&return&render_template('404.html'),&404@app.errorhandler(500)def&internal_server_error(e):
&&&&return&render_template('500.html'),&500@app.route('/',&methods=['GET',&'POST'])def&index():
&&&&form&=&NameForm()
&&&&if&form.validate_on_submit():
&&&&&&&&user&=&User.query.filter_by(username=form.name.data).first()
&&&&&&&&if&user&is&None:
&&&&&&&&&&&&user&=&User(username=form.name.data)
&&&&&&&&&&&&db.session.add(user)
&&&&&&&&&&&&session['known']&=&False
&&&&&&&&else:
&&&&&&&&&&&&session['known']&=&True
&&&&&&&&session['name']&=&form.name.data&&&&&&&&return&redirect(url_for('index'))
&&&&return&render_template('index.html',&form=form,&name=session.get('name'),
&&&&&&&&&&&&&&&&&&&&&&&&&&&known=session.get('known',&False))if&__name__&==&'__main__':
&&&&manager.run()
模板也需要进行相应的修改:
{%&extends&"base.html"&%}{%&import&"bootstrap/wtf.html"&as&wtf&%}{%&block&title&%}Flasky{%&endblock&%}{%&block&page_content&%}&div&class="page-header"&
&&&&&h1&Hello,&{%&if&name&%}{{&name&}}{%&else&%}Stranger{%&endif&%}!&/h1&
&&&&{%&if&not&known&%}
&&&&&p&Pleased&to&meet&you!&/p&
&&&&{%&else&%}
&&&&&p&Happy&to&see&you&again!&/p&
&&&&{%&endif&%}&/div&{{&wtf.quick_form(form)&}}{%&endblock&%}
与python shell集成
在hello.py增加:
from&flask.ext.script&import&Manager,&Shelldef&make_shell_context():
&&&&return&dict(app=app,&db=db,&User=User,&Role=Role)manager.add_command("shell",&Shell(make_context=make_shell_context))
$&python&hello.py&shell
In&[1]:&app
Out[1]:&&Flask&'hello'&In&[2]:&db
Out[2]:&&SQLAlchemy&engine='sqlite:////home/andrew/flasky/data.sqlite'&In&[3]:&User
Out[3]:&__main__.User
使用Flask-Migrate进行数据迁移
flask-migrate基于Alembic,安装:pip install flask-migrate。加入数据迁移一般只需要添加几行:
$&python&hello.py&shellfrom&flask.ext.migrate&import&Migrate,&MigrateCommand#&...migrate&=&Migrate(app,&db)manager.add_command('db',&MigrateCommand)
下面创建数据迁移目录:
$&python&hello.py&db&init
&&Creating&directory&/home/andrew/flasky/migrations&...&done
&&Creating&directory&/home/andrew/flasky/migrations/versions&...&done
&&Generating&/home/andrew/flasky/migrations/env.pyc&...&done
&&Generating&/home/andrew/flasky/migrations/script.py.mako&...&done
&&Generating&/home/andrew/flasky/migrations/README&...&done
&&Generating&/home/andrew/flasky/migrations/alembic.ini&...&done
&&Generating&/home/andrew/flasky/migrations/env.py&...&done
&&Please&edit&configuration/connection/logging&settings&in
&&'/home/andrew/flasky/migrations/alembic.ini'&before&proceeding.
创建自动迁移脚本:
python&hello.py&db&migrate&-m&"initial&migration"INFO&[alembic.migration]&Context&impl&&[alembic.migration]&Will&assume&non-transactional&&[alembic.autogenerate]&Detected&added&table&'roles'INFO&[alembic.autogenerate]&Detected&added&table&'users'INFO&[pare]&Detected&added&index'ix_users_username'&on&'['username']'Generating&/home/flask/flasky/migrations/versions/1bc_initial_migration.py...done$&python&hello.py&db&upgrade
INFO&[alembic.migration]&Context&impl&&[alembic.migration]&Will&assume&non-transactional&&[alembic.migration]&Running&upgrade&None&-&&1bc,&initial&migration
Alembic的手工创建脚本使用命令revision,自动创建使用命令migrate。手工创建生成空的upgrade()和downgrade(),自动创建则可能丢失东西。
作者博客:
& 开源中国(OSChina.NET) |
开源中国社区(OSChina.net)是工信部
指定的官方社区

我要回帖

更多关于 flask 数据库 的文章

 

随机推荐