Python的Flask框架与数据库连接的教程
命令⾏⽅式运⾏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使⽤数据库
我们将使⽤的扩展来管理数据库。由SQLAlchemy项⽬提供的,已封装了关系对象映射(ORM)的⼀个插件。
ORMs允许数据库程序⽤对象的⽅式替代表和SQL语句。⾯向对象的操作被ORM转化为数据库命令。这样就意味着,不⽤sql语句,让为我们执⾏sql语句。
迁移
⼤多数数据库教程都覆盖了创建和使⽤⼀个数据库的⽅法,但是没有充分解决当应⽤程序扩展时数据库更新的问题。通常,你会删除旧的数据库,然后再创建⼀个新的数据库来达到更新的效果,这样就丢失了所有的数据。如果这些数据创建起来很费劲,那么我们不得不写导⼊导出的脚本了。
幸运的是,我们有了更好的⽅案.
我们现在可以使⽤做数据库迁移的更新了,虽然它增加了数据库启动时的负担,但这点⼩⼩的代价还是值得的,毕竟我们不⽤担⼼⼿动迁移数据库的问题了。
理论学习完毕,我们开始吧!
javaswing代码配置
我们的⼩程序使⽤sqlite数据库。sqlite是⼩程序数据库的最佳选择,⼀个可以以单⽂件存储的数据库。
在我们的配置⽂件中添加新的配置项 (fileconfig.py):
import os
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
sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
from app import views, models
注意⽣成的脚本已改动2个地⽅。我们现在开始创建数据库的adb对象,引⽤新的模块。马上来写这个模块。
我们在数据库存储的数据通过数据库model层被映射为⼀些类⾥⾯的对象,ORM层将根据类对象映射到数据库对应的字段.
让我们来创建个映射到users的model。使⽤WWW SQL Designer⼯具,我们创建了代表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
if not ists(SQLALCHEMY_MIGRATE_REPO):
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
else:
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):
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 = w_module('old_model')
old_model = ate_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, a, db.metadata)
open(migration, "wt").write(script)
python入门教程appa = 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表:
posix信号量
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下:
当你在Python命令⾏提⽰符中输⼊下⾯信息:
>>> from app import db, models >>>
这样我们的数据库模块和models就被加载到了内存⾥.
让我们来创建个新⽤户:
>>> u = models.User(nickname='john', email='john@email', role=models.ROLE_USER)
>>> db.session.add(u)
>>> db.sessionmit()
>>>
在同⼀个会话环境下更改数据库,多次的修改可以积累到⼀个会话中最后通过调⽤⼀个db.sessionmit()命令提交,提交同时也保证了原⼦性。如果在会话中出现了错误,会调⽤llback()把数据库回滚到会话之前的状态。如果调⽤的既不是提交也不是回滚,那么系统会默认回滚这个会话。Sessions(会话)保证了数据库的数据⼀致性。
让我们来添加另外⼀个⽤户:
>>> u = models.User(nickname='susan', email='susan@email', role=models.ROLE_USER)
>>> db.session.add(u)
>>> db.sessionmit()
>>>
现在我们可以查询出⽤户信息:
>>> users = models.User.query.all()
>>> print users
[<User u'john'>, <User u'susan'>]
>>> for u in users:
... print u.id,u.nickname
...
1 john
2 susan
>>>
此处我们使⽤了query查询函数,在所有的model类中都可以使⽤这个函数。注意id是如何⾃动⽣成的。
还有另外⼀种⽅式来查询,如果我们知道了⽤户的id,我们可以使⽤下⾯的⽅式查⽤户信息:
>>> u = models.(1)
>>> print u
<User u'john'>
>>>
现在让我们添加⼀条微博信息:
>>> import datetime
>>> u = models.(1)
>>> p = models.Post(body='my first post!', timestamp=datetime.datetime.utcnow(), author=u)
>>> db.session.add(p)
>>> db.sessionmit()
这个地⽅我们把时间设置为UTC时区,所有的存储在数据库⾥的时间将是UTC格式,⽤户可能在世界各地写微博,因此我们需要使⽤统⼀的时间单位。在以后的教程中我们将学习如何在⽤户本地时区使⽤这些时间。
你也许注意到我们没有在Post类中设置user_id字段,取⽽代之的是把⽤户对象存储到了author字段。auhtor字段是个通过Flask-SQLAlchemy 添加的虚拟字段⽤来建⽴关联关系的,我们之前已经定义好了这个名字,参照:model中的db.relationship中backref参数。通过这些信
息,ORM层就能知道如何取到user_id。
要完成这个会话,让我们来看看更多可做的数据库查询:
# get all posts from a user
>>> u = models.(1)
>>> print u
<User u'john'>
>>> posts = u.posts.all()
>>> print posts
[<Post u'my first post!'>]
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论