Flask中的数据库

Flask-SQLAlchemy

  • 这个插件为流行的SQLAlchemy包做了一层封装以便在Flask中调用更方便,

ORM:

  • 类似SQLAlchemy这样的包叫做Object Relational Mapper,简称ORM。
  • ORM允许应用程序使用高级实体(如类,对象和方法)而不是表和SQL来管理数据库。 ORM的工作就是将高级操作转换成数据库命令.

Flask-Migrate

  • 这个插件是Alembic的一个Flask封装,是SQLAlchemy的一个数据库迁移框架。

安装Flask-SQLAlchemy插件:
  1. (venv) $ pip install flask-sqlalchemy
  2. (venv) $ pip install flask-migrate

Flask-SQLAlchemy配置

SQLite数据库
  • SQLite数据库是开发小型乃至中型应用最方便的选择,因为每个数据库都存储在磁盘上的单个文件中
  1. # 配置文件添加两个新的配置项 config.py
  2. import os
  3. basedir = os.path.abspath(os.path.dirname(__file__))
  4. class Config(object):
  5. # ...
  6. SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
  7. 'sqlite:///' + os.path.join(basedir, 'app.db')
  8. SQLALCHEMY_TRACK_MODIFICATIONS = False
  • Flask-SQLAlchemy插件从SQLALCHEMY_DATABASE_URI配置变量中获取应用的数据库的位置。 当回顾第三章可以发现,首先从环境变量获取配置变量,未获取到就使用默认值,这样做是一个好习惯。 本处,我从DATABASE_URL环境变量中获取数据库URL,如果没有定义,我将其配置为basedir变量表示的应用顶级目录下的一个名为app.db的文件路径。

  • SQLALCHEMY_TRACK_MODIFICATIONS配置项用于设置数据发生变更之后是否发送信号给应用,我不需要这项功能,因此将其设置为False。

  1. # app/__init__.py
  2. from flask import Flask
  3. from config import Config
  4. from flask_sqlalchemy import SQLAlchemy
  5. from flask_migrate import Migrate
  6. app = Flask(__name__)
  7. app.config.from_object(Config)
  8. db = SQLAlchemy(app)
  9. # db对象来表示数据库
  10. migrate = Migrate(app, db)
  11. # 数据库迁移引擎migrate
  12. from app import routes, models
  13. # models的模块,这个模块将会用来定义数据库结构。

用户表数据库

4.Flask数据库 - 图1

  1. # app/models.py
  2. from app import db
  3. # User类继承自db.Model,它是Flask-SQLAlchemy中所有模型的基类
  4. # 类将表的字段定义为类属性,字段被创建为db.Column类的实例
  5. class User(db.Model):
  6. id = db.Column(db.Integer, primary_key=True)
  7. username = db.Column(db.String(64), index=True, unique=True)
  8. email = db.Column(db.String(120), index=True, unique=True)
  9. password_hash = db.Column(db.String(128))
  10. def __repr__(self):
  11. return '<User {}>'.format(self.username)
  • 该类的repr方法用于在调试时打印用户实例。在下面的Python交互式会话中你可以看到repr()方法的运行情况:
  1. >>> from app.models import User
  2. >>> u = User(username='susan', email='susan@example.com')
  3. >>> u
  4. <User susan>

创建数据库迁移数据库

  • flask db init来创建microblog的迁移存储库:
  1. (venv) $ flask db init
  2. Creating directory /home/miguel/microblog/migrations ... done
  3. Creating directory /home/miguel/microblog/migrations/versions ... done
  4. Generating /home/miguel/microblog/migrations/alembic.ini ... done
  5. Generating /home/miguel/microblog/migrations/env.py ... done
  6. Generating /home/miguel/microblog/migrations/README ... done
  7. Generating /home/miguel/microblog/migrations/script.py.mako ... done
  8. Please edit configuration/connection/logging settings in
  9. '/home/miguel/microblog/migrations/alembic.ini' before proceeding.
  • 运行迁移初始化命令之后,你会发现一个名为migrations的新目录。该目录中包含一个名为versions的子目录以及若干文件.

第一次数据库迁移
  • flask db migrate子命令生成这些自动迁移:
  1. (venv) $ flask db migrate -m "users table"
  2. INFO [alembic.runtime.migration] Context impl SQLiteImpl.
  3. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  4. INFO [alembic.autogenerate.compare] Detected added table 'user'
  5. INFO [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']'
  6. INFO [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']'
  7. Generating /home/miguel/microblog/migrations/versions/e517276bb1c2_users_table.py ... done
  • 有两个函数叫upgrade()和downgrade()。 upgrade()函数应用迁移,downgrade()函数回滚迁移。
  • flask db migrate命令不会对数据库进行任何更改,只会生成迁移脚本。 要将更改应用到数据库,必须使用flask db upgrade命令。
  1. (venv) $ flask db upgrade
  2. INFO [alembic.runtime.migration] Context impl SQLiteImpl.
  3. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  4. INFO [alembic.runtime.migration] Running upgrade -> e517276bb1c2, users table
  • 因为本应用使用SQLite,所以upgrade命令检测到数据库不存在时,会创建它(在这个命令完成之后,你会注意到一个名为app.db的文件,即SQLite数据库)。 在使用类似MySQL和PostgreSQL的数据库服务时,必须在运行upgrade之前在数据库服务器上创建数据库。

数据库关系

4.Flask数据库 - 图2
— 修改后的app/models.py如下:

  1. from datetime import datetime
  2. from app import db
  3. class User(db.Model):
  4. id = db.Column(db.Integer, primary_key=True)
  5. username = db.Column(db.String(64), index=True, unique=True)
  6. email = db.Column(db.String(120), index=True, unique=True)
  7. password_hash = db.Column(db.String(128))
  8. posts = db.relationship('Post', backref='author', lazy='dynamic')
  9. def __repr__(self):
  10. return '<User {}>'.format(self.username)
  11. class Post(db.Model):
  12. # 新的“Post”类表示用户发表的动态
  13. id = db.Column(db.Integer, primary_key=True)
  14. body = db.Column(db.String(140))
  15. timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
  16. # timestamp字段将被编入索引,如果你想按时间顺序检索用户动态
  17. """
  18. 我还为其添加了一个default参数,并传入了datetime.utcnow函数。
  19. 当你将一个函数作为默认值传入后,SQLAlchemy会将该字段设置为调用该函数的值(请注意,在utcnow之后我没有包含(),所以我传递函数本身,而不是调用它的结果
  20. """
  21. user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
  22. def __repr__(self):
  23. return '<Post {}>'.format(self.body)
  • 在服务应用中使用UTC日期和时间是推荐做法。 这可以确保你使用统一的时间戳,无论用户位于何处,这些时间戳会在显示时转换为用户的当地时间。
  • 迁移数据库
  1. (venv) $ flask db migrate -m "posts table"
  2. INFO [alembic.runtime.migration] Context impl SQLiteImpl.
  3. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  4. INFO [alembic.autogenerate.compare] Detected added table 'post'
  5. INFO [alembic.autogenerate.compare] Detected added index 'ix_post_timestamp' on '['timestamp']'
  6. Generating /home/miguel/microblog/migrations/versions/780739b227a7_posts_table.py ... done
  • 更新数据库
  1. (venv) $ flask db upgrade
  2. INFO [alembic.runtime.migration] Context impl SQLiteImpl.
  3. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  4. INFO [alembic.runtime.migration] Running upgrade e517276bb1c2 -> 780739b227a7, posts table