使用Flask的数据库插件实现增删改查

Flask本身不支持数据库,绝大多数的数据库都提供了Python客户端包,它们之中的大部分都被封装成Flask插件以便更好地和Flask应用结合。数据库被划分为两大类,遵循关系模型的一类是关系数据库,另外的则是非关系数据库,简称NoSQL。关系数据库更适合具有结构化数据的应用程序,例如用户列表,用户动态等,而NoSQL数据库往往更适合非结构化数据。

1、安装数据库插件Flask-SQLAlchemy\Flask-Migrate

pip install flask-sqlalchemy
pip install flask-migrate
pip install sqlalchemy-migrate
第一个是Flask-SQLAlchemy,这个插件为流行的SQLAlchemy包做了一层封装以便在Flask中调用更方便,类似SQLAlchemy这样的包叫做Object Relational Mapper,简称ORM。 ORM允许应用程序使用高级实体(如类,对象和方法)而不是表和SQL来管理数据库,ORM的工作就是将高级操作转换成数据库命令。
SQLAlchemy不只是某一款数据库软件的ORM,而是支持包含MySQL、PostgreSQL和SQLite在内的很多数据库软件。你可以在开发的时候使用简单易用且无需另起服务的SQLite,需要部署应用到生产服务器上时,则选用更健壮的MySQL或PostgreSQL服务,并且不需要修改应用代码,只需修改应用配置。
第二个是数据库迁移插件Flask-Migrate,当需要对现有数据库更新或者添加表结构时, 这是一项困难的工作,因为关系数据库是以结构化数据为中心的,所以当结构发生变化时,数据库中的已有数据需要被迁移到修改后的结构中。Flask-Migrate是SQLAlchemy的一个数据库迁移框架。 使用数据库迁移增加了启动数据库时候的一些工作,但这对将来的数据库结构稳健变更来说,是一个很小的代价。
注:新版的flask抛弃了flask.ext这种引入扩展的方法,更改为 flask_扩展名
例如:
以前:from flask.ext.script import Manager
现在:from flask_script import Manager

2、Flask-SQLAlchemy及Migrate配置

开发阶段,我会使用SQLite数据库,SQLite数据库是开发小型乃至中型应用最方便的选择,因为每个数据库都存储在磁盘上的单个文件中,并且不需要像MySQL和PostgreSQL那样运行数据库服务。
让我们给配置文件添加两个新的配置项:

import os
basedir = os.path.abspath(os.path.dirname(__file__))#获取顶级目录

class Config(object):
    # ...
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')
        #我从DATABASE_URL环境变量中获取数据库URL,如果没有定义,我将其配置为basedir变量表示的应用顶级目录下的一个名为app.db的文件路径
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    #SQLALCHEMY_TRACK_MODIFICATIONS配置项用于设置数据发生变更之后是否发送信号给应用

数据库在应用的表现形式是一个数据库实例,数据库迁移引擎同样如此。它们将会在应用实例化之后进行实例化和注册操作。

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)#给应用app添加了一个db对象来表示数据库
migrate = Migrate(app, db)#给应用app和原数据库db添加数据库迁移引擎migrate

from app import routes, models#在底部导入了一个名为models的模块,这个模块将会用来定义数据库结构

3、数据库模型

定义数据库中一张表及其字段的,通常叫做数据模型。ORM(SQLAlchemy)会将类的实例关联到数据库表中的数据行,并翻译相关操作。
下图是一张用户模型表,用来设计用户表的各个字段:
在这里插入图片描述
用户表用代码实现,并存储到新建的模块app/models.py中,代码如下:

from app import db
User类继承db.Model类,是Flask-SQLAlchemy中所有模型的基类。将表的字段定义为类属性,字段被创建为db.Column类的实例,它传入字段类型以及其他可选参数。
class User(db.Model):
#id字段通常存在于所有模型并用作**主键**。每个用户都会被数据库分配一个id值,并存储到这个字段中。大多数情况下,主键都是数据库自动赋值的,我只需要提供id字段作为主键即可。
    id = db.Column(db.Integer, primary_key=True)
    #username,email和password_hash字段被定义为字符串。
    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))#使用哈希密码,这就大大提高了安全性

    def __repr__(self):#用于在调试时打印用户实例。
        return '<User {}>'.format(self.username) 

4、创建数据库迁移存储库

随着应用的不断增长,很可能会新增、修改或删除数据库结构。 Alembic(Flask-Migrate使用的迁移框架)将以一种不需要重新创建数据库的方式进行数据库结构的变更。
维护一个数据库迁移存储库,它是一个存储迁移脚本的目录。 每当对数据库结构进行更改后,都需要向存储库中添加一个包含更改的详细信息的迁移脚本。 当应用这些迁移脚本到数据库时,它们将按照创建的顺序执行。
通过在pycharm的terminal执行flask db init子命令,可以完成自动microblog迁移存储库migrations的创建,其中versions子目录用来存储数据库迁移脚本。

5、第一次数据库迁移

有两种方法来创建数据库迁移:手动或自动。 要自动生成迁移,Alembic会将数据库模型定义的数据库模式与数据库中当前使用的实际数据库模式进行比较。 然后,使用必要的更改来生成迁移脚本,以使数据库模式与应用程序模型匹配。
当前情况是,由于之前没有数据库,自动迁移将把整个User模型添加到迁移脚本中。
flask db migrate子命令生成这些自动迁移: -m可选参数为迁移添加了一个简短的注释。

(flask) D:\microblog_git>flask db migrate -m "users table"
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'user'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username
']'
Generating D:\microblog_git\migrations\versions\711a6124fa73_users_table.py ...  done

之后的输出表明检测到了一个用户表(table)和两个索引(index)。 然后它会告诉你迁移脚本的输出路径。
输出的迁移脚本有两个函数叫upgrade()和downgrade()。 upgrade()函数应用迁移,downgrade()函数回滚迁移。 Alembic通过使用降级方法可以将数据库迁移到历史中的任何点,甚至迁移到较旧的版本。
flask db migrate命令不会对数据库进行任何更改,只会生成迁移脚本。 要将更改应用到数据库,必须使用flask db upgrade命令。

对于SQLite数据库,upgrade命令检测到数据库不存在时,会自动创建它,在这个命令完成之后,你会注意到一个名为app.db(在Microblog路径下)的文件,即SQLite数据库。

6、数据库升级和降级流程

通过数据库迁移机制的支持,在你修改应用中的模型之后,将生成一个新的迁移脚本(flask db migrate),你可能会审查它以确保自动生成的正确性,然后将更改应用到你的开发数据库(flask db upgrade)。 测试无误后,将迁移脚本添加到源代码管理并提交。
flask db downgrade命令可以回滚上次的迁移。你可能已经生成了一个迁移脚本并将其应用,只是发现所做的更改并不完全是你所需要的。在这种情况下,可以降级数据库,删除迁移脚本,然后生成一个新的来替换它。

7、数据库关系

关系数据库擅长存储数据项之间的关系。如下两张表:
在这里插入图片描述

标记谁写了一个给定的动态的最有效的方法是链接两个相关的记录。一旦建立了users与posts之间的关系,数据库就可以在查询中展示两个表的关系。例如:当你看到一个动态,可以查询user是谁;或者当你好奇一个user时,可以查询它所有的动态。
post表将具有必须的id、用户动态的body和timestamp字段。 除了这些预期的字段之外,我还添加了一个user_id字段,将该用户动态链接到其作者。 你已经看到所有用户都有一个唯一的id主键, 将用户动态链接到其作者的方法是添加对用户id的引用,这正是user_id字段所在的位置。 这个user_id字段被称为外键
上面的数据库图显示了外键作为该字段和它引用的表的id字段之间的链接。 这种关系被称为一对多,因为“一个”用户写了“多”条动态。

from datetime import datetime
from app import db

class User(db.Model):
    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类有一个新的posts字段,用db.relationship初始化。这不是实际的数据库字段,而是用户和其动态之间关系的高级视图,因此它不在数据库图表中。对于一对多关系,db.relationship字段通常在“一”的这边定义,并用作访问“多”的便捷方式。例如:如果我有一个用户实例u,表达式u.posts将运行一个数据库查询,返回该用户发表过的所有动态。
    #db.relationship的第一个参数表示代表关系“多”的类。 backref参数定义了代表“多”的类的实例反向调用“一”的时候的属性名称。这将会为用户动态添加一个属性post.author,调用它将返回给该用户动态的用户实例。 
    def __repr__(self):
        return '<User {}>'.format(self.username)
#新的“Post”类表示用户发表的动态。
class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    #timestamp字段将被编入索引,如果你想按时间顺序检索用户动态,这将非常有用。 我还为其添加了一个default参数,并传入了datetime.utcnow函数。 当你将一个函数作为默认值传入后,SQLAlchemy会将该字段设置为调用该函数的值(请注意,在utcnow之后我没有包含(),所以我传递函数本身,而不是调用它的结果)。 通常,在服务应用中使用UTC日期和时间是推荐做法。 这可以确保你使用统一的时间戳,无论用户位于何处,这些时间戳会在显示时转换为用户的当地时间。
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    #user_id字段被初始化为user.id的外键,这意味着它引用了来自用户表的id值。本处的user是数据库表的名称,Flask-SQLAlchemy自动设置类名为小写来作为对应表的名称。

    def __repr__(self):
        return '<Post {}>'.format(self.body)

更了应用模型,就需要生成一个新的数据库迁移:flask db migrate -m "posts table"
并将这个迁移应用到数据库: flask db upgrade

8、使用数据库-增、删、改、查

由于应用还没有任何数据库逻辑,所以让我们在Python解释器中使用以便熟悉它。

对数据库的更改是在会话的上下文中完成的,你可以通过db.session进行访问验证。 允许在会话中累积多个更改,一旦所有更改都被注册,你可以发出一个指令db.session.commit()来写入所有更改。 如果在会话执行的任何时候出现错误,调用db.session.rollback()会中止会话并删除存储在其中的所有更改。要记住的重要一点是,只有在调用db.session.commit()时才会将更改写入数据库。

    from app import db
>>> from app.models import User, Post
#创建新用户
>>> u = User(username='john', email='john@example.com')
>>> db.session.add(u)
>>> db.session.commit()
#查询所有用户
>>> users = User.query.all()
>>> users
#另一种查询方式-利用id
>>> u = User.query.get(id)
>>> u

#添加用户动态
>>> u = User.query.get(1)
>>> p = Post(body='my first post!', author=u)
>>> db.session.add(p)
>>> db.session.commit()

#db.relationship给User添加Post类,给post类添加author属性。
posts = db.relationship('Post',backref='author',lazy = 'dynamic')

#清空数据库
>>> 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()

9、flask shell

当使用flask shell时,该命令预先导入应用实例。
在terminal打开flask shell:

app

flask shell的绝妙之处不在于它预先导入了app,而是你可以配置一个“shell上下文”,也就是可以预先导入一份对象列表。
在microblog.py中实现一个函数,它通过添加数据库实例和模型来创建了一个shell上下文环境:

from app import app, db
from app.models import User, Post
#app.shell_context_processor装饰器将该函数注册为一个shell上下文函数。当flask shell命令运行时,它会调用这个函数并在shell会话中注册它返回的项目。
@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User, 'Post': Post}

如果运行以上的db, User, Post命令,报 NameError异常,说明 make_shell_context() 没有被Flask注册。最有可能的原因是你的环境变量中没有设定 FLASK_APP=microblog.py

在终端输入:
set FLASK_APP=microblog_git.py
flask shell
进行shell测试

无需每次在终端set环境变量,在项目的根目录下新建一个名为 .flaskenv 的文件,其内容是:
FLASK_APP=microblog.py,通过此项设置,FLASK_APP就可以自动加载了。

在终端输入:
flask run
运行app

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值