chapter5 - Web数据库

5.1 Python数据库框架

Flask允许自己选择需要的数据库框架,但在选择时,应考虑这些因素:

  • 易用性 这里要注重的两个概念是ORM或ODM,也就是对象关系映射和对象文档映射。它们用于把高层的面向对象操作转换成低层的数据库指令。
  • 性能
  • 可移植性 是否可在多个平台中平移
  • Flask集成度 使用集成了Flask的框架可以简化配置和操作

因此本书选择的是Flask-SQLAlchemy,这个Flask扩展包装了SQLAlchemy框架

5.2 使用Flask-SQLAlchemy管理数据库

安装

可使用pip安装:(venv) $ pip install flask-sqlalchemy。在Flask-SQLAlchemy中,数据库使用URL指定,也就是以URL的形式来连接数据库,而不是sql原生的connect之类的操作。具体格式如下表:

数据库引擎 URL MySQL mysql://username:password@hostname/database Postgres postgresql://user SQLite (Unix) sqlite:////absolute/path/to/database SQLite (Windows) sqlite:///c:/absolute/path/to/database

hostname表示MySQL服务所在的主机,可以是本地主机或远程服务器 database表示要使用的数据库名称 username和password表示需要有到的数据库用户密令

本书使用的是SQLite数据库,它不需要服务器,因此URL中的database是硬盘上的文件名

配置数据库

程序使用的数据库URL必须保存到Flask配置对象的SQLALCHEMY_DATABASE_URI中,另外还有个要设置的是SQLALCHEMY_COMMIT_ON_TEARDOWN键,将其设为True时,每次执行SQL请求后会自动提交。

经个人实测发现,如果仅做了以上两个设置,在运行时会有警告:

/home/cavin/Code/Python/flask/lib64/python3.5/site-packages/flask_sqlalchemy/__init__.py:800: UserWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.
  warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True to suppress this warning.')

按提示说的,把这个变量配置成True即可消除这个警告:

app.config[SQLACHEMY_TRACK_MODIFICANTS'] = True

因此,配置后的hello.py文件为:

from flask_sqlalchemy import SQLAlchemy

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config[SQLALCHEMY_DATABASE_URI'] = sqlite:///' + os.path.join(basedir,data.sqlite'SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config[SQLALCHEMY_TRACK_MODIFICANTS True

db = SQLAlchemy(app)

注意以上(除了app = Flask(__name__))外都是新添加到文件中的内容,其它内容不变basedir变量可以获取当前文件的绝对路径。

db是SQLAlchemy类的一个实例,表示程序使用的数据库,同时也获得了Flask-SQLAlchemy提供的所有功能。

5.3 定义模型

其实这里所说到的“模型”,在某种程度上可看做是传统SQL中的“数据表”。如下可定义Role和User模型:

class Role(db.Model):
    __tablename__ = roles
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(64),unique=True)

    def __repr__(self):
        return <Role %r>' % self.name

 User(db.Model):
    users True)
    username = db.Column(db.String(64),unique=True,index=<User %r>' % self.username

其中,__tablename__定义了在数据库中使用的表名。其余的id、name、username这些都是模型的属性,相当于数据表中的字段,用db.Column构造函数实现,其第一个参数是数据库列和模型属性的类型。

模型中的__repr()__方法并不是强制要求的,这里定义了并返回一个具有可读性的字符串表示模型,可方便调试和测试。 常用的SQLAlchemy列类型如下:

类型名 Python类型 说明 Integer int 普通整数,一般是32位 SmallInteger int 取值范围小的整数,一般是16位 BigInteger int或long 不限制精度的整数 Float float 浮点数 Numeric decimal.Decimal 定点数 String str 变长字符串 Text str 变长字符串,对较长或不限长度的字符串做了优化 Unicode unicode 变长Unicode字符串 UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化 Boolean bool 布尔值 Date datetime.date 日期 Time datetime.time 时间 DateTime datetime.datetime 日期和时间 Interval datetime.timedelta 时间间隔 Enum str 一组字符串 PickleType 任何Python对象 自动使用Pickle序列化 LargeBinary str 二进制文件

db.Column中其余的参数指定属性的配置选项,常用的可选项如下:

选项名 说明 primary_key 如果设置为True,这列就是表的主键 unique 如果设置为True,这列不允许出现复生值 index 如果设置为True,为这列创建索引 nullable 如果设置为True,这列允许为空;为False则不允许空 default 为这列定义默认值

Flask-SQLAlchemy要求每个模型都要定义主键,通常命名为id

5.4 关系

关系也即不同表之间的联系。例如,一个论坛中的用户有其自己的id,其归属于一个分组,这个组也有组的id,用户信息表和分组表可以通这两个各自的id来关联起来。一个组可以有多个用户,但一个用户同时只能归属于一个组。这就是一对多的关系。 在模型中表示如下: 修改hello.py

 Role(db.Model):
    # 之前的内容
    users = db.relationship(User',backref=role)

 User(db.Model):
     之前的内容
    role_id = db.Column(db.Integer,db.ForeignKey(roles.id'))

User模型中的role_id表被定义为外键,建立起了关系,传给db.ForeignKey()的参数roles.id表明,这列的值是roles表中的id值。

在Role模型中,users属性返回与角色相关联的用户组成的列表,db.relationship()的第一个参数表明这个关系连接的是哪一个表。第二个参数backref向User模型添加了一个role属性,定义了反向关系。 定义关系时(db.relationship())常用的配置选项有:

选项名 说明 backref 在关系的另一个模型中添加反向引用 primaryjoin 明确指定两个模型之间使用的联结条件 lazy 指定如何加载相关记录。

可选值有select-首次访问时按需加载;immediate-源对象加载后就加载;joined-加载记录,但使用联结;subquery-立即加载,但使用子查询;noload-永不加载、dynamic-不加载记录,但提供加载记录的查询 uselist 如果设为False,不使用列表,而使用标量值 order_by 指定关系中记录的排序方式 secondary 指定多对多关系中关系表的名字 secondaryjoin SQLAlchemy无法自行决定时,指定多对多关系中的二级联结条件、

5.5 数据库操作

5.5.1 创建表

(venv) $ python hello.py shell
>>> from hello  db
>>> db.create_all()

此时,就可在程序目录中新建了一个名为data.sqlite的文件。

5.5.2 插入数据

>>>  Role,User
>>> admin_role = Role(name=Admin)
>>> mod_role = Role(name=Moderator)
>>> user_role = Role(name=)
>>> user_john = User(username=johnadmin_role)
>>> user_susan = User(username=susanuser_role)
>>> user_david = User(username=david
>>> db.session.add(admin_role)
>>> db.session.add(mod_role)
>>> db.session.add(user_role)
>>> db.session.add(user_john)
>>> db.session.add(user_susan)
>>> db.session.add(user_david)

或简写方式:

>>> db.session.add_all([admin_role,mod_role,user_role,user_john,user_susan,user_david])

写入数据库:

>>> db.session.commit()

应注意的是,这里的数据库会话db.session和第4章中提到的Flask session对象没有关系,它只是数据库会话,或称为事务。数据库会话可以保持数据库操作的一致性。 当然,它也有回滚:db.session.rollback()

 

 

5.6 使用Flask-Migrate实现数据库迁移

所谓数据库迁移,其实主要是数据库(包含其结构与字段属性等)的更新。比如说新增了表、字段等。使用Flask-Migrate可以很好地实现数据库平移更新,不改变原有结构和数据,也不需要删除重建。

5.6.1 创建迁移仓库

先要安装Flask-Migrate:

(venv) $ pip install flask-migrate

在程序入口文件hello.py中增加对数据库迁移的支持:

from flask_migrate  Migrate,MigrateCommand

 .其它原有代码

migrate = Migrate(app,db)
manager.add_command(db,MigrateCommand)

 其它代码

然后才是使用init子命令来创建迁移仓库:

(venv) $ python hello.py db init

这个命令会创建migrations文件夹,所有的迁移脚本都会放在里面

5.6.2 创建迁移脚本

(venv) $ python hello.py db migrate -m "initial migration"

5.6.3 更新数据库

(venv) $ python hello.py db update

这里应该要提出的是,在本书后面,作者在需要进行更新数据库的时候,只会提到需要读者执行更新数据库的命令:python hello.py update,却没有说到需要执行创建迁移脚本命令。这是因为作者默认读者是从本书的github仓库上pull代码的,而pull下来的代码里面已经包含有数据库迁移脚本了,所以不需要再创建。如果是自己更新代码而不是从github上下载代码的话,那么就必须要先执行创建迁移脚本的命令,然后再执行更新数据库的命令才会有效。切记!

相关文章

Jinja2:是Python的Web项目中被广泛应用的模板引擎,是由Pyt...
监听QQ消息并不需要我们写代码,因为市面上已经有很多开源QQ...
【Flask框架】—— 视图和URL总结
python+web+flask轻量级框架的实战小项目。登录功能,后续功...
有了这个就可以配置可信IP,关键是不需要企业认证,个人信息...