Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程

使用Flask-SQLAlchemy管理数据库
Flask-SQLAlchemy是一个Flask扩展,它简化了在Flask应用程序中对SQLAlchemy的使用。SQLAlchemy是一个强大的关系数据库框架,支持一些数据库后端。提供高级的ORM和底层访问数据库的本地SQL功能。
和其他扩展一样,通过pip安装Flask-SQLAlchemy:

(venv) $ pip install flask-sqlalchemy

在Flask-SQLAlchemy,数据库被指定为URL。表格列出三个最受欢迎的数据库引擎url的格式:

在这些URL中,hostname是指托管MySQL服务的服务器,可能是本地(localhost)又或是远程服务器。数据库服务器可以托管多个数据库,所以database指出要使用的数据库名。数据库需要身份验证,username和 password是数据库用户凭证。
注:> SQLite数据库没有服务,所以hostname、username和password可以缺省且数据库是一个磁盘文件名。
应用程序数据库URL必须在Flask配置对象中的SQLALCHEMY_DATABASE_URI键中进行配置。另一个有用的选项是SQLALCHEMY_COMMIT_ON_TEARDOWN,可以设置为True来启用自动提交数据库更改在每个请求中。查阅Flask-SQLAlchemy文档获取更多其他配置选项。

from flask.ext.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')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

db = SQLAlchemy(app)

由SQLAlchemy实例化的db对象表示数据库且提供访问Flask-SQLAlchemy的所有功能。

模型定义
模型是指由应用程序使用的持久化实体。在ORM的背景下,一个模型通常是一个带有属性的Python类,其属性与数据库表的列相匹配对应。Flask-SQLAlchemy数据库实例提供了一个基类以及一组辅助类和函数用于定义它的结构。

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

class User(db.Model):
  __tablename__ = 'users'
  id = db.Column(db.Integer, primary_key=True)
  username = db.Column(db.String(64), unique=True, index=True)

def __repr__(self):
  return '<User %r>' % self.username

__tablename__类变量定义数据库中表的名称。如果__tablename__缺省Flask-SQLAlchemy会指定默认的表名,但是这些缺省名称不遵守使用复数命名的约定,所以最好是显式命名表名。其余的变量是模型的属性,被定义为db.Column类的实例。
传给db.Column构造函数的第一个参数是数据库列的类型也就是模型属性的数据类型。表格5-2列出一些可用的列的类型,也是用于模型中的Python类型。

最常见的SQLAlchemy列类型
db.Column剩余的参数为每个属性指定了配置选项。

最常见的SQLAlchemy列选项
注:Flask-SQLAlchemy需要给所有的模型定义主键列,通常命名为id。
两个模型都包含了repr()方法来给它们显示一个可读字符串,虽然不是完全必要,不过用于调试和测试还是很不错的。

关系
关系数据库通过使用关系在不同的表中建立连接。关系图表达了用户和用户角色之间的简单关系。这个角色和用户是一对多关系,因为一个角色可以从属于多个用户,而一个用户只能拥有一个角色。
下面的模型类展示了中表达的一对多关系。

class Role(db.Model):
  # ...
  users = db.relationship('User', backref='role')

class User(db.Model):
  # ...
  role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

关系通过使用外键来连接两行。添加给User模型的role_id列被定义为外键,且建立关系。db.ForeignKey()的参数roles.id指定的列应该理解为在roles表的行中持有id值的列。
添加到Role模型的users属性表现了关系的面向对象的观点。给定Role类的实例,users属性会返回一组连接到该角色的用户。指定给db.relationship()的第一个参数表明模型中关系的另一边。如果类还未定义,这个模型可以作为字符串提供。
注意:之前在segmentdefault中遇到的问题,后来粗略阅读了SQLAlchemy的源码。ForeignKey类的column接收三种类型的参数,一种是“模型名.属性名”;一种是“表名.列名”,最后一种没看明白,下次试着用一下。
db.relationship()的backref参数通过给User模型增加role属性来定义反向关系。这个属性可以替代role_id访问Role模型,是作为对象而不是外键。
大多数情况下db.relationship()可以定位自己的外键关系,但是有时候不能确定哪个列被用作外键。例如,如果User模型有两个或更多列被定义为Role的外键,SQLAlchemy将不知道使用两个中的哪一个。每当外键配置模棱两可的时候,就必须使用额外参数db.relationship()。下标列出一些常用配置选项用于定义关系:

常用SQLAlchemy关系选项

建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 5a来切换到这个版本的应用程序。
除了一对多关系还有其他种类关系。一对一关系可以表述为前面描述的一对多关系,只要将db.relationship()中的uselist选项设置为False,“多”就变为“一”了。多对一关系也可表示为将表反转后的一对多关系,或表示为外键和db.relationship()定义在“多”那边。最复杂的关系类型,多对多,需要一个被称作关联表的额外表。你将在第十二章学习多对多关系。

数据库操作
学习怎样使用模型的最好方式就是使用Python shell。以下部分将介绍最常见的数据库操作。

创建表

首先要做的第一件事情就是指示Flask-SQLAlchemy基于模型类创建数据库。db.create_all()函数会完成这些:

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

如果你检查应用程序目录,你会发现名为data.sqlite的新文件,SQLite数据库名在配置中给出。如果数据库已存在db.create_all()函数不会重新创建或更新数据库表。这会非常的不方便当模型被修改且更改需要应用到现有的数据库时。更新现有的数据库表的蛮力解决方案是先删除旧的表:

>>> db.drop_all()
>>> db.create_all()

不幸的是,这种方法有个不受欢迎的副作用就是摧毁旧的数据库中的所有数据。更新数据库问题的解决方案会在这章快结束的时候介绍。

插入行

下面的示例会创建新的角色和用户:

>>> from hello import Role, User
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)

模型的构造函数接受模型属性的初始值作为关键字参数。注意,甚至可以使用role属性,即使它不是一个真正的数据库列,而是一对多关系的高级表示。这些新对象的id属性没有显式设置:主键由Flask-SQLAlchemy来管理。到目前为止对象只存于Python中,他们还没有被写入数据库。因为他们的id值尚未分配:

>>> print(admin_role.id) None
>>> print(mod_role.id) None
>>> print(user_role.id) None

修改数据库的操作由Flask-SQLAlchemy提供的db.session数据库会话来管理。准备写入到数据库中的对象必须添加到会话中:

>>> 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])

为了写对象到数据库,需要通过它的commit()方法来提交会话:

>>> db.session.commit()

再次检查id属性;这个时候它们都已经被设置好了:

>>> print(admin_role.id)
1
>>> print(mod_role.id)
2
>>> print(user_role.id)
3

注:db.session数据库会话和第四章讨论的Flask会话没有任何联系。数据库会话也叫事务。
数据库会话在数据库一致性上是非常有用的。提交操作会原子性地将所有添加到会话中的对象写入数据库。如果在写入的过程发生错误,会将整个会话丢弃。如果你总是在一个会话提交相关修改,你必须保证避免因部分更新导致的数据库不一致的情况。

注:数据库会话也可以回滚。如果调用db.session.rollback(),任何添加到数据库会话中的对象都会恢复到它们曾经在数据库中的状态。
修改行

数据库会话中的add()方法同样可以用于更新模型。继续在同一shell会话中,下面的示例重命名“Admin”角色为“Administrator”:

>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()

注意:不过貌似我们在做更新操作的时候都不使用db.session.add(),而是直接使用db.session.commit()来提交事务。
删除行

数据库会话同样有delete()方法。下面的示例从数据库中删除“Moderator”角色:

>>> db.session.delete(mod_role)
>>> db.session.commit()

注意删除,和插入更新一样,都是在数据库会话提交后执行。

返回行

Flask-SQLAlchemy为每个模型类创建一个query对象。最基本的查询模型是返回对应的表的全部内容:

>>> Role.query.all()
[<Role u'Administrator'>, <Role u'User'>]
>>> User.query.all()
[<User u'john'>, <User u'susan'>, <User u'david'>]

使用过滤器可以配置查询对象去执行更具体的数据库搜索。下面的例子查找所有被分配“User”角色的用户:

>>> User.query.filter_by(role=user_role).all()
[<User u'susan'>, <User u'david'>]

对于给定的查询还可以检查SQLAlchemy生成的原生SQL查询,并将查询对象转换为一个字符串:

>>> str(User.query.filter_by(role=user_role))
'SELECT users.id AS users_id, users.username AS users_username,
users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'

如果你退出shell会话,在前面的示例中创建的对象将不能作为Python对象而存在,但可继续作为行记录存在各自的数据库表中。如果你开始一个全新的shell会话,你必须从它们的数据库行中重新创建Python对象。下面的示例执行查询来加载名字为“User”的用户角色。

>>> user_role = Role.query.filter_by(name='User').first()

过滤器如filter_by()通过query对象来调用,且返回经过提炼后的query。多个过滤器可以依次调用直到需要的查询配置结束为止。

下面展示一些查询中常用的过滤器。

在需要的过滤器已经全部运用于query后,调用all()会触发query执行并返回一组结果,但是除了all()以外还有其他方式可以触发执行。常用SQLAlchemy查询执行器:

关系的原理类似于查询。下面的示例从两边查询角色和用户之间的一对多关系:

>>> users = user_role.users
>>> users
[<User u'susan'>, <User u'david'>]
>>> users[0].role
<Role u'User'>

此处的user_role.users查询有点小问题。当user_role.users表达式在内部调用all()时通过隐式查询执行来返回用户的列表。因为查询对象是隐藏的,是不可能通过附加查询过滤器进一步提取出来。在这个特定的例子中,它可能是用于按字母排列顺序返回用户列表。在下面的示例中,被lazy = 'dynamic'参数修改过的关系配置的查询是不会自动执行的。

app/models.py:动态关系

class Role(db.Model):
  # ...
  users = db.relationship('User', backref='role', lazy='dynamic')
  # ...

用这种方式配置关系,user_roles.user查询还没有执行,所以可以给它增加过滤器:

>>> user_role.users.order_by(User.username).all()
[<User u'david'>, <User u'susan'>]
>>> user_role.users.count()
2
(0)

相关推荐

  • Flask数据库迁移简单介绍

    前言 用过Django的小伙伴都知道,Django的ORM是自带的,比较特殊,而且集成了很多功能,比如数据库迁移- 何为ORM,个人之见解,简化sql语句的书写,将关系型数据库的一张张表转化为了python的类,最大的好处是简化了学习成本,不会sql语句的程序员也能平滑的使用数据库,并且天生防sql注入.flask可以使用SQLAlchemy,包名为flask-sqlalchemy.至于具体用法翻译的官方文档讲的也足够清楚.大家直接谷歌跟着文档就能搞定,本篇主要讲如何进行数据库迁移. flask

  • Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程

    我们在升级系统的时候,经常碰到需要更新服务器端数据结构等操作,之前的方式是通过手工编写alter sql脚本处理,经常会发现遗漏,导致程序发布到服务器上后无法正常使用. 现在我们可以使用Flask-Migrate插件来解决之,Flask-Migrate插件是基于Alembic,Alembic是由大名鼎鼎的SQLAlchemy作者开发数据迁移工具. 具体操作如下: 1. 安装Flask-Migrate插件 $ pip install Flask-Migrate 2. 修改Flask App部分的代

  • Python的Flask框架与数据库连接的教程

     命令行方式运行Python脚本 在这个章节中,我们将写一些简单的数据库管理脚本.在此之前让我们来复习一下如何通过命令行方式执行Python脚本. 如果Linux 或者OS X的操作系统,需要有执行脚本的权限.例如: chmod a+x script.py 该脚本有个指向使用解释器的命令行.再脚本赋予执行权限后就可以通过命令行执行,就像这样: like this: ./script.py <arguments> 然而,在Windows系统上这样做是不行的,你必须提供Python解释器作为必选参

  • Python的Flask框架中实现简单的登录功能的教程

     回顾 在前面的系列章节中,我们创建了一个数据库并且学着用用户和邮件来填充,但是到现在我们还没能够植入到我们的程序中. 两章之前,我们已经看到怎么去创建网络表单并且留下了一个实现完全的登陆表单. 在这篇文章中,我们将基于我门所学的网络表单和数据库来构建并实现我们自己的用户登录系统.教程的最后我们小程序会实现新用户注册,登陆和退出的功能. 为了能跟上这章节,你需要前一章节最后部分,我们留下的微博程序.请确保你的程序已经正确安装和运行. 在前面的章节,我们开始配置我们将要用到的Flask扩展.为了登

  • Python的Flask框架中集成CKeditor富文本编辑器的教程

    CKeditor是目前最优秀的可见即可得网页编辑器之一,它采用JavaScript编写.具备功能强大.配置容易.跨浏览器.支持多种编程语言.开源等特点.它非常流行,互联网上很容易找到相关技术文档,国内许多WEB项目和大型网站均采用了CKeditor. 下载CKeditor 访问CKeditor官方网站,进入下载页面,选择Standard Package(一般情况下功能足够用了),然后点击Download CKEditor按钮下载ZIP格式的安装文件.如果你想尝试更多的功能,可以选择下载Full

  • 在Python的web框架中编写创建日志的程序的教程

    在Web开发中,后端代码写起来其实是相当容易的. 例如,我们编写一个REST API,用于创建一个Blog: @api @post('/api/blogs') def api_create_blog(): i = ctx.request.input(name='', summary='', content='') name = i.name.strip() summary = i.summary.strip() content = i.content.strip() if not name: r

  • 在Python的Tornado框架中实现简单的在线代理的教程

    实现代理的方式很多种,流行的web服务器也大都有代理的功能,比如http://www.tornadoweb.cn用的就是nginx的代理功能做的tornadoweb官网的镜像. 最近,我在开发一个移动运用(以下简称APP)的后台程序(Server),该运用需要调用到另一平台产品(Platform)的API.对于这个系统来说,可选的一种实现方式方式是APP同时跟Server&Platform两者交互:另一种则在Server端封装掉Platform的API,APP只和Server交互.显然后一种方式

  • Python的Flask框架中的Jinja2模板引擎学习教程

    Flask的模板功能是基于Jinja2模板引擎来实现的.模板文件存放在当前目前下的子目录templates(一定要使用这个名字)下. main.py 代码如下: from flask import Flask, render_template app = Flask(__name__) @app.route('/hello') @app.route('/hello/<name>') def hello(name=None): return render_template('hello.html

  • Flask框架中request、请求钩子、上下文用法分析

    本文实例讲述了Flask框架中request.请求钩子.上下文用法.分享给大家供大家参考,具体如下: request 就是flask中代表当前请求的request对象: 常用的属性如下: 属性 说明 类型 data 记录请求的数据,并转换为字符串 * form 记录请求中的表单数据 MultiDict args 记录请求中的查询参数 MultiDict cookies 记录请求中的cookie信息 Dict headers 记录请求中的报文头 EnvironHeaders method 记录请求

  • Flask框架中的session设置详解

    目录 Flask中的session session设置 关于session的设置 cookies中的session session序列化机制 session反序列化机制 session的使用和验证 Flask中的session session设置 Flask除请求对象之外,还有一个 session 对象. 它允许你在不同请求间存储特定用户的信息.它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥. 我们知道,在django中的session

  • flask框架中的cookie和session使用

    文章介绍了flask框架中的cookie和session.Session是在服务器端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群.数据库.文件中.Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式. WEB -> cookie & session 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协

  • Python常用Web框架Django、Flask与Tornado介绍

    web框架是什么? web开发框架是一组工具,同时也提供了非常多的资源,供软件开发人员构建和管理网站.提供web服务.编写web应用程序.它是一个抽象工具,能使开发应用程序和重复使用代码的过程变得更容易. 为什么需要web开发框架? 进行自定义软件开发时,web开发框架让整个过程变得更容易,因而开发速度更快.更高效.许多开发框架都很便宜,这也使得整个客户机的成本更小. 一个被广泛使用的框架安全性更高.这主要依赖于该框架背后的社区,在这里,用户成为长期的测试人员,并可以提供修复. 如果发现了安全漏

随机推荐