3.数据库
1.flask中的数据库
数据库迁移,使用 SQLAlchemy-migrate 来跟踪数据库的更新。这只是在开始建立数据库的时候比较花费工作时间,以后就再不用人工进行数据的迁移了。
1
2$ source flask/bin/activate
$ pip install SQLAlchemy-migrate数据库配置,针对我们小型的应用,我们将采用 sqlite 数据库,sqlite 数据库是小型应用的最方便的选择,每一个数据库都是存储在单个文件里,这里对config.py进行再次配置。
1
2
3
4
5import os
basedir = os.path.abspath(os.path.dirname(__file__))
sqlalchemy_database_url = 'sqlite:///' + os.path.join(basedir, 'app.db')
sqlalchemy_migrate_repo = os.path.join(basedir, 'db_repository')sqlalchemy_database_url是 Flask-aqlalchemy 扩展需要的,存储我们数据库文件的路径,sqlalchemy_migrate_repo 是文件夹,存储数据库文件。对init.py文件更新。
1
2
3
4
5
6
7
8from 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创建了一个 db 对象,这是我们的数据库,接着导入一个新的模块,叫做 models。
数据库模型(app/models.py)
1
2
3
4
5
6
7
8
9
10
11
12
13#-*- coding:utf-8 -*-
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 类包含一些字段,这些字段被定义成类的变量,repr 方法告诉 Python 如何打印这个类的对象。
创建数据库,创建数据库脚本文件(db_create.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#-*- coding:utf-8 -*-
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))运行这个脚本文件,python db_create.py,运行完后在app下会发现新的app.db文件,这是一个空的sqlite数据库,创建后就支持迁移,还有一个db_repository文件,这是SQLAlchemy-migrate 存储它的数据文件的地方。
2.第一次迁移
这是我们第一次迁移,我们将从一个空数据库迁移到一个能存储用户的数据库上,用db_migrate.py实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#-*- coding:utf-8 -*-
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)
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)与我们模型的结构(app/models.py),两者间的不同将会被记录成一个迁移脚本存放在迁移仓库中。
- 数据库的升级db_upgrade.py和回退db_downgrade.py
1 | from migrate.versioning import api |
如果有数据库迁移的支持,当你准备发布新版的时候,你只需要录制一个新的迁移,拷贝迁移脚本到生产服务器上接着运行脚本,所有事情就完成了,数据库升级也只需要一点 Python 脚本。1
2
3
4
5
6
7from 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))
这个脚本会回退数据库一个版本,可以运行多次来回退多个版本。
- 数据库关系
连接用户和他们写的 blog。方式就是通过在 posts 增加一个字段,这个字段包含了编写 blog 的用户的 id。这个 id 称为一个外键,users 表中的 id 与 posts 表中的 user_id,这种关系称为一对多,一个用户编写多篇 blog。
对模板进行修改,(app/models.py)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23from 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 类,这是用来表示用户编写的 blog。在 Post 类中的 user_id 字段初始化成外键,因此让 Flask-SQLAlchemy 知道这个字段是连接到用户上。
3.数据库操作
- 首先创建一个新用户名为john
1
2
3
4
5
6
7$ python
# 进入交互式界面
>>> from app import db
>>> from app.models import User, Post, ROLE_USER, ROLE_ADMIN
>>> u1 = User(nickname='john', email='john@email.com', role=ROLE_USER)
>>> db.session.add(u1)
>>> db.session.commit()
在会话的上下文中完成对数据库的更改。多个的更改可以在一个会话中累积,当所有的更改已经提交,你可以发出一个db.session.commit(),这能原子地写入更改。如果在会话中出现错误的时候, db.session.rollback() 可以使得数据库回到会话开始的状态;若没有 commit 也没有 rollback 发生,系统默认情况下会回滚会话。会话保证数据库将永远保持一致的状态。
添加另一个用户susan
1
2
3'susan', email='susan@email.com', role=ROLE_USER) u2 = User(nickname=
db.session.add(u2)
db.session.commit()查询用户
1
2
3
4
5
6
7
8
9users = User.query.all()
print users [<User u'john'>, <User u'susan'>]
for u in users:
... print u.id,u.nickname
...
1 john
2 susan
>>>提交一篇 blog
1
2
3
4
5import datetime
1) u = User.query.get(
'my first post!', timestamp=datetime.datetime.utcnow(), author=u) p = Post(body=
db.session.add(p)
db.session.commit()
设置我们的 timestamp 为UTC 时区,所有存储在数据库的时间戳都会是 UTC,世界上不同地方的用户因此需要有个统一的时间单位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1) u = User.query.get(
print u
<User u'john'>
posts = u.posts.all()
print posts
[<Post u'my first post!'>]
# 获得john的个人信息和所有博客内容
for p in posts:
print p.id,p.author.nickname,p.body
...
1 john my first post!
2) u = User.query.get(
print u
<User u'susan'>
print u.posts.all()