2019独角兽企业重金招聘Python工程师标准>>>

Flask 中的数据库

我们将使用 Flask-SQLAlchemy 扩展来管理我们应用程序的数据。这个扩展封装了 SQLAlchemy 项目,这是一个 对象关系映射器 或者 ORM。

ORMs 允许数据库应用程序与对象一起工作,而不是表以及 SQL。执行在对象的操作会被 ORM 翻译成数据库命令。这就意味着我们将不需要学习 SQL,我们将让 Flask-SQLAlchemy 代替 SQL。

安装:pip install Flask-SQLAlchemy

迁移

我见过的大多数数据库教程会涉及到创建和使用一个数据库,但没有充分讲述随着应用程序扩大更新数据库的问题。通常情况下,每次你需要进行更新,你最终不得不删除旧的数据库和创建一个新的数据库,并且失去了所有的数据。如果数据不能容易地被重新创建,你可能会被迫自己编写导出和导入脚本。

幸运地,我们还有一个更好的选择。

我们将使用 SQLAlchemy-migrate 来跟踪数据库的更新。它只是在开始建立数据库的时候多花费些工作,这只是很小的代价,以后就再不用担心人工数据迁移了。

安装:pip install SQLAlchemy-migrate

配置

针对我们小型的应用,我们将采用 sqlite 数据库。sqlite 数据库是小型应用的最方便的选择,每一个数据库都是存储在单个文件里。

我们有许多新的配置项需要添加到配置文件中(文件 config.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 是 Flask-SQLAlchemy 扩展需要的。这是我们数据库文件的路径。

SQLALCHEMY_MIGRATE_REPO 是文件夹,我们将会把 SQLAlchemy-migrate 数据文件存储在这里。

最后,当我们初始化应用程序的时候,我们也必须初始化数据库。这是我们更新后的初始化文件(文件 app/__init__.py):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)from app import views, models

注意我们在初始化脚本中的两个改变。创建了一个 db 对象,这是我们的数据库,接着导入一个新的模块,叫做 models。接下来我们将编写这个模块。

数据库模型

我们存储在数据库中数据将会以类的集合来表示,我们称之为数据库模型。ORM 层需要做的翻译就是将从这些类创建的对象映射到适合的数据库表的行。

让我们创建一个表示用户的模型。使用 WWW SQL Designer 工具,我制作如下的图来表示我们用户的表:

id 字段通常会在所有模型中,并且用于作为主键。在数据库的每一个用户会被赋予一个不同的 id 值,存储在这个字段中。幸好这是自动完成的,我们仅仅需要的是提供 id 这个字段。

nickname 以及 email 字段是被定义成字符串,并且指定了最大的长度以便数据库可以优化空间占用。

现在我们已经决定用户表的样子,剩下的工作就是把它转换成代码(文件 app/models.py):

from app import dbclass 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)def __repr__(self):return '<User %r>' % (self.nickname)

我们刚刚创建的 User 类包含一些字段,这些字段被定义成类的变量。字段是被作为 db.Column 类的实例创建的,db.Column 把字段的类型作为参数,并且还有一些其它可选的参数,比如表明字段是否唯一。

__repr__ 方法告诉 Python 如何打印这个类的对象。我们将用它来调试。

创建数据库

配置以及模型都已经到位了,是时候准备创建数据库文件。SQLAlchemy-migrate 包自带命令行和 APIs,这些 APIs 以一种将来允许容易升级的方式来创建数据库。我发现命令行使用起来比较别扭,因此我们自己编写一些 Python 脚本来调用迁移的 APIs。

这是创建数据库的脚本(文件 db_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)
else:api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))

为了创建数据库,你需要运行这个脚本(记得如果在 Windows 上命令有些不同):

./db_create.py

在运行上述命令之后你会发现一个新的 app.db 文件。这是一个空的 sqlite 数据库,创建一开始就支持迁移。同样你还将有一个 db_repository 文件夹,里面还有一些文件,这是 SQLAlchemy-migrate 存储它的数据文件的地方。请注意,我们不会再生的存储库,如果它已经存在。这将使我们重新创建数据库,同时保留现有的存储库,如果我们需要。

第一次迁移

现在,我们已经定义了我们的模型,我们可以将其合并到我们的数据库中。我们会把应用程序数据库的结构任何的改变看做成一次迁移,因此这是我们第一次迁移,我们将从一个空数据库迁移到一个能存储用户的数据库上。

为了实现迁移,我们需要编写一小段 Python 代码(文件 db_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
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
migration = SQLALCHEMY_MIGRATE_REPO + ('/versions/%03d_migration.py' % (v+1))
tmp_module = imp.new_module('old_model')
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
exec(old_model, 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)
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('New migration saved as ' + migration)
print('Current database version: ' + str(v))

脚本看起来很复杂,其实际上做的并不多。SQLAlchemy-migrate 迁移的方式就是比较数据库(在本例中从 app.db 中获取)与我们模型的结构(从文件 app/models.py 获取)。两者间的不同将会被记录成一个迁移脚本存放在迁移仓库中。迁移脚本知道如何去迁移或撤销它,所以它始终是可能用于升级或降级一个数据库。

然而在使用上面的脚本自动地完成迁移的时候也不是没有问题的,我见过有时候它很难识别新老格式的变化。为了让 SQLAlchemy-migrate 容易地识别出变化,我绝不会重命名存在的字段,我仅限于增加或者删除模型或者字段,或者改变已存在字段的类型。当然我一直会检查生成的迁移脚本,确保它是正确。

毋庸置疑你不应该在没有备份下去尝试迁移数据库。当然也不能在生产环境下直接运行迁移脚本,必须在开发环境下确保迁移运转正常。

因此让我们继续进行,记录下迁移:

./db_migrate.py

脚本的输出如下:

New migration saved as db_repository/versions/001_migration.py
Current database version: 1

脚本会打印出迁移脚本存储在哪里,也会打印出目前数据库版本。空数据库的版本是0,在我们迁移到包含用户的数据库后,版本为1.

数据库升级和回退

到现在你可能想知道为什么完成记录数据库迁移的这项令人麻烦的事情是这么重要。

假设你有一个应用程序在开发机器上,同时有一个拷贝部署在到线上的生产机器上。在下一个版本中,你的数据模型有一个变化,比如新增了一个表。如果没有迁移脚本,你可能必须要琢磨着如何修改数据库格式在开发和生产机器上,这会花费很大的工作。

如果有数据库迁移的支持,当你准备发布新版的时候,你只需要录制一个新的迁移,拷贝迁移脚本到生产服务器上接着运行脚本,所有事情就完成了。数据库升级也只需要一点 Python 脚本(文件 db_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)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('Current database version: ' + str(v))

当你运行上述脚本的时候,数据库将会升级到最新版本。

通常情况下,没有必要把数据库降低到旧版本,但是,SQLAlchemy-migrate 支持这么做(文件 db_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)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('Current database version: ' + str(v))

这个脚本会回退数据库一个版本。你可以运行多次来回退多个版本。

数据库关系

关系型数据可以很好的存储数据项之间的关系。考虑一个用户写了一篇 blog 的例子。在 users 表中有一条用户的数据,在 posts 表中有一条 blog 数据。记录是谁写了这篇 blog 的最有效的方式就是连接这两条相关的数据项。

一旦在用户和文章(post)的联系被建立,有两种类型的查询是我们可能需要使用的。最常用的查询就是查询 blog 的作者。复杂一点的查询就是一个用户的所有的 blog。Flask-SQLAlchemy 将会帮助我们完成这两种查询。

让我们扩展数据库以便存储 blog。为此我们回到数据库设计工具并且创建一个 posts 表。

我们的 posts 表中有必须得 id 字段,以及 blog 的 body 以及一个 timestamp。这里没有多少新东西。只是对 user_id 字段需要解释下。

我们说过想要连接用户和他们写的 blog。方式就是通过在 posts 增加一个字段,这个字段包含了编写 blog 的用户的 id。这个 id 称为一个外键。我们的数据库设计工具把外键显示成一个连线,这根连线连接于 users 表中的 id 与 posts 表中的 user_id。这种关系称为一对多,一个用户编写多篇 blog。

让我们修改模型以反映这些变化(app/models.py):

from app import dbclass 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)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 类,这是用来表示用户编写的 blog。在 Post 类中的 user_id 字段初始化成外键,因此 Flask-SQLAlchemy 知道这个字段是连接到用户上。

值得注意的是我们已经在 User 类中添加一个新的字段称为 posts,它是被构建成一个 db.relationship 字段。这并不是一个实际的数据库字段,因此是不会出现在上面的图中。对于一个一对多的关系,db.relationship 字段通常是定义在“一”这一边。在这种关系下,我们得到一个 user.posts 成员,它给出一个用户所有的 blog。不用担心很多细节不知道什么意思,以后我们会不断地看到例子。

首先还是来运行迁移脚本:

./db_migrate.py

输出:

New migration saved as db_repository/versions/002_migration.py
Current database version: 2

编程时间

我们花了很多时间定义我们的数据库,但是我们仍没有看到它是如何工作的。因为我们的应用程序中还没有关于数据库的代码,让我们先在 Python 解释器上试用下我们全新的数据库。

让我们先启动 Python。在 Linux 或者 OS X 上:

flask/bin/python

或者在 Windows 上:

flask\Scripts\python

一旦启动 Python,在 Python 提示符中输入如下语句:

>>> from app import db, models
>>>

这将会把我们的数据库和模型载入内存中。

首先创建一个新用户:

>>> u = models.User(nickname='john', email='john@email.com')
>>> db.session.add(u)
>>> db.session.commit()
>>>

在会话的上下文中完成对数据库的更改。多个的更改可以在一个会话中累积,当所有的更改已经提交,你可以发出一个 db.session.commit(),这能原子地写入更改。如果在会话中出现错误的时候, db.session.rollback() 可以是数据库回到会话开始的状态。如果即没有 commit 也没有 rollback 发生,系统默认情况下会回滚会话。会话保证数据库将永远保持一致的状态。

让我们添加另一个用户:

>>> u = models.User(nickname='susan', email='susan@email.com')
>>> db.session.add(u)
>>> db.session.commit()
>>>

现在我们可以查询用户:

>>> users = models.User.query.all()
>>> users
[<User u'john'>, <User u'susan'>]
>>> for u in users:
...     print(u.id,u.nickname)
...
1 john
2 susan
>>>

对于查询用户,我们使用 query 成员,这是对所有模型类都是可用的。

这是另外一种查询。如果你知道用户的 id ,我们能够找到这个用户的数据像下面这样:

>>> u = models.User.query.get(1)
>>> u
<User u'john'>
>>>

现在让我们提交一篇 blog:

>>> 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)
>>> db.session.commit()

这里我们设置我们的 timestamp 为 UTC 时区。所有存储在数据库的时间戳都会是 UTC。我们有来自世界上不同地方的用户因此需要有个统一的时间单位。在后面的教程中会以当地的时间呈现这些时间在用户面前。

你可能注意到了我们并没有设置 user_id 字段。相反我们在 author 字段上存储了一个 User 对象。ORM 层将会知道怎么完成 user_id 字段。

让我们多做一些查询:

# get all posts from a user
>>> u = models.User.query.get(1)
>>> u
<User u'john'>
>>> posts = u.posts.all()
>>> 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)
>>> u
<User u'susan'>
>>> u.posts.all()
[]# get all users in reverse alphabetical order
>>> models.User.query.order_by('nickname desc').all()
[<User u'susan'>, <User u'john'>]
>>>

Flask-SQLAlchemy 文档可能会提供更多有帮助的信息。

在结束之前,需要清除一下刚才创建的数据,以便在下一章中会有一个干净的数据库:

>>> 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)
...
>>> db.session.commit()
>>>

结束语

这是一个漫长的教程。我们已经学会了使用数据库的基本知识,但我们还没有纳入到我们的应用程序的数据库。在下一章中,我们将会把我们所学到的所有关于数据库的知识用于实践。

转载于:https://my.oschina.net/u/3767248/blog/1620990

web框架flask(4)——数据库相关推荐

  1. python web框架flask连接mysql数据库操作

    最近在用flask框架做一个python web项目,其中要连接数据库,所以总结如下: 如何用pycharm快速创建一个flask项目我就不详细说了,可以先创建虚拟环境,不过大部分的事pycharm都 ...

  2. Web框架——Flask系列之Flask-SQLAlchemy数据库的基本操作(九)

    数据库基本操作 在Flask-SQLAlchemy中,插入.修改.删除操作,均由数据库会话管理. 会话用 db.session 表示.在准备把数据写入数据库前,要先将数据添加到会话中然后调用db.se ...

  3. python微型web框架flask介绍

    Flask是一个基于python的,微型web框架.之所以被称为微型是因为其核心非常简单,同时具有很强的扩展能力.它几乎不给使用者做任何技术决定. 安装flask时应该注意其必须的几个支持包比如Jin ...

  4. Web框架——Flask系列之Flask简介(一)

    一.Web应用程序作用 Web(World Wide Web)诞生最初的目的,是为了利用互联网交流工作文档 二.关于Web框架 (一)什么是Web框架? 已经封装好了一段代码,协助程序快速开发,相当于 ...

  5. 基于python的web框架Flask实现SDEAM游戏商城设计 文档+前后台源码

    资源下载地址:https://download.csdn.net/download/sheziqiong/85660942 资源下载地址:https://download.csdn.net/downl ...

  6. Python明星web框架Flask

    Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug和模 ...

  7. Python超级明星WEB框架Flask

    Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug和模 ...

  8. python django flask介绍_Python开发WEB框架Flask详细介绍

    Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug和模 ...

  9. 框架详解_详解:python Web框架flask表单实现(建议收藏)

    今天为大家带来的内容是:python框架flask表单实现详解 文章内容主要介绍了python框架flask表单实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需 ...

最新文章

  1. 对gridview中的一些操作。
  2. 【.net web】iframe小问题的解决
  3. 克隆虚拟机后需要修改的注意项
  4. 微软宣布公开预览Dev Spaces for AKS
  5. c语言 字符串map,C语言实现BitMap
  6. 亚信安全“双引擎”AI技术 亮相2017华为全联接大会
  7. js 的 protype 小总结
  8. (4)Spring框架----依赖注入(DI)
  9. HeroM2连击技能设置和DB完整数据
  10. R语言 软件安装 源码安装R
  11. [Deprecated!] Android开发案例 - 微博正文
  12. w10 桌面计算机垃圾桶,解答win10我的电脑怎么放在桌面
  13. 数据结构 栈的结构特点及基本操作
  14. 奇偶性与魔术(一)——奇偶性的数学本质
  15. vs2019报错 声明与xxxx不兼容
  16. mysql表字段一定要写英文吗_关于数据库列名是否必须是英文的?
  17. google浏览器打开出现“喔唷,崩溃啦”解决办法
  18. expect免密登录
  19. 累了,困了,来看“MySQL”,让你梦回吹角连营
  20. 基于模型的设计及其嵌入式实现(无水印).pdf_DeepMind打造最强表示学习模型BigBiGAN,Goodfellow点赞!...

热门文章

  1. 页面重新跳转到父类url
  2. 使用jquery获取url以及jquery获取url参数的方法
  3. 【剑指offer】Q38:数字在数组中出现的次数
  4. hdu4282 x^z+y^z+x*y*z=k 解的个数
  5. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )
  6. 【错误记录】Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | 仅做参考 | 没有解决实际问题 )
  7. 【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )
  8. 【Android 热修复】热修复原理 ( 多 Dex 打包机制 | 多 Dex 支持 | Dex 分包设置 | 开发和产品风格设置 | 源码资源 )
  9. 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )
  10. VM VirtualBox 虚拟机linux系统 登录密码忘记 ,重置