The Flask Mega-Tutorial Part IV: Database
Database in Flask
Flask本身并不支持数据库,但这给使用者提供了一个自由选择的权利
数据库可分为两大类,关系型(SQL)和非关系型(NoSQL,不能使用查询语言SQL)
Flask-SQLAlchemy,它是一个ORM(Object-relational mapping,对象关系映射),是对SQLAlchemy包的一个Flask封装扩展包。ORM允许应用抽象高的实体像类,对象,方法取代表和SQL去管理数据库。ORM的任务就是把这些抽象度高的运算转化成低层的数据库命令
SQLAlchemy支持大多数数据库,包括MySQL,PostgreSQL,SQLite等。这意味着更换数据库对你的应用影响是很小的。
Databas Migrations
关系型数据库是以结构化数据为中心的,所以当结构改变了,那么数据也需要跟着改变以适应新的结构。Flask-Migrate,对Alembic封装后的Flask extension.一个数据库迁移框架。
在 __init__.py里添加下面代码
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy(app)
migrate = Migrate(app,db)
(venv) $ flask db init #创建migration仓库
(venv) $ flask db migrate -m "users table"#automatic migrations
#versions文件夹里会生成一.py文件,文件里有两个函数upgrade()和downgrade(),upgrade()应用migration,downgrade()移除它
(venv)$ flask db migrade
命令并未对数据库做出任何改变,它只是生成一份迁移代码。flask db upgrade命令才是。
数据库关系
关系型数据库能很好的存储数据项之间的关系。现假设有个使用写了条博客评论,那么该使用者就有一条记录在使用者表(user table),该条评论也会作为一条记录在posts table.
#models.py修改为:
from datetime import datetime
from app import db
class User(db.Model):#User table
id = db.Column(db.Integer, primary_key=True)#主键
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
#表明了user与Post表之间是一对多的关系,假设有一user u,则u.posts会返回u提交的所有Post
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):#Post table
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)#index=True,表明timestamp是可以按索引检索
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
#db.ForeignKey('user.id')表明user_id是user表属性id(user.id)的引用
def __repr__(self):
return '<Post {}>'.format(self.body)
由于更新了应用models(models.py),需要重新生成新的database migration并应用到数据库
flask db migrate -m "posts table"
flask db upgrade #应用到数据库
数据库操作
在venv环境下运行python
from app import db#从包里导入数据库实例
from app.models import User,Post#导入两个数据库表类
创建一个新的user
u= User(username = 'john',email = 'john@example.com')
db.session.add(u)
u= User(username = 'susan',email = 'susan@example.com')
db.session.add(u)
db.session.commit()#类似git
#db.session.rollback()是撤销更改
获取user
user = User.query.all()
所有models都有一个query属性,该属性是运行数据库查询的入口。
添加一条blog post
u = User.query.get(1)
p = Post(body = 'my first post',author = u)
#添加了一个新属性author
db.session.add(p)
db.sessiom.commit()
查询blog post
u= User.query.get(1)
posts=u.posts.all()
posts=Post.query.all()
for p in posts:
print(p.id,p.author,p.body)
清楚记录
users = User.query.all()
for u in users:
db.session.delete(u)
posts = Post.query.all()
for p in posts:
db.session.delete(p)
db.session.commit()
Shell Context
从上面的测试我们知道,我们经常要在venv环境下运行python,而这又得重新导入
from app import dn
from app.models import User,Post
这是一项烦琐的事,flask shell命令可以解决这个问题,该命令会启动一个基于应用环境的Python解释器,具体请看下面的例子:
flask shell #该命令会预先导入应用实例,在本例子中就是导入app instance;flask shell不单有这好处,而且可以用于配置一个"shell context",里面包含其他预导入
#microblog.py修改为:
from app import app,db
from app.models import User,Post
#该装饰器使得被装饰函数make_shell_context作为shell context function,
@app.shell_context_processor
def make_shell_context():
return {'db':db,'User':User,'Post':Post}

浙公网安备 33010602011771号