Flask-Grundlegende Inhalte-Sechs

I、Flask数据关联模型SQLAlchemy

Flask-SQLAlchemy使用起来非常有趣,对于基本应用十分容易使用,并且对于大型项目易于扩展。有关完整的指南,请参见SQLAlchemyAPI文档。

常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy 对象时候把 Flask 应用传递给它作为参数。

一旦创建,这个对象就包含 sqlalchemysqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model的类,用于作为声明模型时的 delarative 基类

普通的 SQLAlchemy 不同之处:
1. SQLAlchemy 允许您访问下面的东西 :
·sqlalchemysqlalchemy.orm 下所有的函数和类
·一个叫做 session的预配置范围的会话(session)
·metadata 属性
·engine 属性
·SQLAlchemy.create_all()SQLAlchemy.drop_all(),根据模型用来创建以及删除表格的方法
· 一个 Model 基类,即是一个已配置的声明(declarative)的基础(base)
2. Model 声明基类行为类似一个常规的 Python 类,不过有个query属性,可以用来查询模型 (ModelBaseQuery)
3. 您必须提交会话,但是没有必要在每个请求后删除它(session),Flask-SQLAlchemy 会帮您完成删除操作。

\color{violet}{在了解以上问题后,这一章我们将用ORM的力量优化数据库操作。}

什么是ORM?
ORM是用来把对象模型表示的对象映射到基于SQL的关系模型数据库结构中去。简明点说,数据库中每一行就是一个对象。


II、相关的配置字段

在上一章内,我们在db_demo内应用课许多诸如app.config['SQLALCHEMY_DATABASE_URI'] = xxx的语句,这些语句为SQLAlchemy的配置方式

那么常见的配置选项有

选项 说明
SQLALCHEMY_DATABASE_URI 用于连接的数据库 URI 。例如:sqlite:////tmp/test.db 或 mysql://username:password@server/db
SQLALCHEMY_BINDS 一个映射 binds 到连接 URI 的字典。更多 binds 的信息见 用 Binds 操作多个数据库 。
SQLALCHEMY_ECHO 如果设置为 Ture , SQLAlchemy 会记录所有 发给 stderr 的语句,这对调试有用。
SQLALCHEMY_RECORD_QUERIES 可以用于显式地禁用或启用查询记录。查询记录 在调试或测试模式自动启用。更多信息见 get_debug_queries() 。
SQLALCHEMY_NATIVE_UNICODE 可以用于显式禁用原生 unicode 支持。当使用不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上某些版本的 PostgreSQL )。
SQLALCHEMY_POOL_SIZE 数据库连接池的大小。默认是引擎默认值(通常 是 5 )
SQLALCHEMY_POOL_TIMEOUT 设定连接池的连接超时时间。默认是 10 。
SQLALCHEMY_POOL_RECYCLE 多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定这个值为 2 小时。

III、常见数据模型字段

类型名 python中类型 说明
Integer int 普通整数,一般是32位
SmallInteger int 取值范围小的整数,一般是16位
BigInteger int/long 不限制精度的整数
Float float 浮点数
Numeric decimal.Decimal 普通整数,一般是32位
String str 变长字符串
Text str 变长字符串,对较长或不限长度的字符串做了优化
Unicode unicode 变长Unicode字符串
UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 时间
Time datetime.datetime 日期和时间
LargeBinary str 二进制文件

IV、约束字段与表间关系

由于Flask-SQLAlchemy不自动设置表内的字段的约束与,因此需要在设计数据字段时自行添加

那么常见的约束字段有

选项名 说明
primary_key 如果为True,代表表的主键
unique 如果为True,代表这列不允许出现重复的值
index 如果为True,为这列创建索引,提高查询效率
nullable 如果为True,允许有空值,如果为False,不允许有空值
default 为这列定义默认值
autoincrement 自增

由于不自行设计id值(与Django的一定区别)Flask-SQLAlchemy并不涉及到表间关系的自动配置,因此需要了解表间关系联系字段

常见的表间关系字段有

选项名 说明
backref 在关系的另一模型中添加反向引用,用于设置外键名称,在1内查n的
primary join 明确指定两个模型之间使用的联结条件
uselist 如果为False,不使用列表,而使用标量值
order_by 指定关系中记录的排序方式
secondary 指定多对多关系中关系表的名字
secondary join 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件
lazy 指定如何加载相关记录。

lazy的可选值有

属性 说明
select 首次访问时按需加载
immediate 源对象加载后就加载
joined 加载记录,但使用联结
subquery 立即加载,但使用子查询
noload 永不加载
dynamic 不加载记录,但提供加载记录的查询

V、查询执行器

执行器 说明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果没有结果,则返回 None
first_or_404() 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应
get() 返回指定主键对应的行,如果没有对应的行,则返回 None
get_or_404() 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应
count() 返回查询结果的数量
paginate() 返回一个 Paginate 对象,它包含指定范围内的结果

VI、过滤器

过滤器 说明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit 使用指定的值限定原查询返回的结果
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询


filter_by用于查询简单的列名,不支持比较运算符。
比filter_by的功能更强大,支持比较运算符,支持or_、in_等语法。


VII、一个简单的建表实例

我们基于上一章中的db_demo.py的内容展开,在每一个修改的实例内增加一个__repr__方法,简单介绍一下__repr__:当我们需要对一个函数中传入某个参数后的状态需要打包打印,或者显示在屏幕上时我们可以在这个函数内部定义一个__repr__函数,可以通过这个函数,对你想要打印的内容进行设置。

修改db_demo.py

class Role(db.Model):
    # 定义表名
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    db.relationship('User', backref='role')

    def __repr__(self):
        return 'Role:{}'.format(self.name)


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(64), unique=True)
    password = db.Column(db.String(64), unique=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return 'User:{}'.format(self.name)


VIII、一些查询的联系

在此前,我们修改db_demo内的main,以用来插入一些数据,当然,这种方式并非日后的常用方法,这里只作为一种简单的填充

if __name__ == '__main__':
    # 删除表
    db.drop_all()
    # 创建表
    db.create_all()
    ro1 = Role(name='admin')
    ro2 = Role(name='user')
    db.session.add_all([ro1, ro2])
    db.session.commit()
    us1 = User(name='wang', email='wang@163.com', password='123456', role_id=ro1.id)
    us2 = User(name='zhang', email='zhang@189.com', password='201512', role_id=ro2.id)
    us3 = User(name='chen', email='chen@126.com', password='987654', role_id=ro2.id)
    us4 = User(name='zhou', email='zhou@163.com', password='456789', role_id=ro1.id)
    us5 = User(name='tang', email='tang@neuedu.com', password='158104', role_id=ro2.id)
    us6 = User(name='wu', email='wu@gmail.com', password='5623514', role_id=ro2.id)
    us7 = User(name='qian', email='qian@gmail.com', password='1543567', role_id=ro1.id)
    us8 = User(name='liu', email='liu@neuedu.com', password='867322', role_id=ro1.id)
    us9 = User(name='li', email='li@163.com', password='4526342', role_id=ro2.id)
    us10 = User(name='sun', email='sun@163.com', password='235523', role_id=ro2.id)
    db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10])
    db.session.commit()
    db.session.add_all([us1, us2])
    db.session.commit()
    app.run(port=5005,debug=True)

1、返回名字等于wang 的所有人

    User.query.filter_by(name='wang').all()

2、返回查询的第一个对象

    User.query.first()

3、返回所有查询对象

    User.query.all()

4、返回名字结尾字符为g的所有数据

    User.query.filter(User.name.endswith("g")).all()
    User.query.filter(User.name.startswith("w")).all()
    User.query.filter(User.name.contains("n")).all()
    User.query.filter(User.name.like("%n%g")).all()

5、返回user中id = 1的数据

    User.query.get(1)

6、返回名字不等于wang的所有数据

    User.query.filter(not_(User.name == "wang")).all()
    User.query.filter(User.name != "wang").all()

7、返回名字不等于 wang 且 邮箱 以 163.com结尾的所有

    User.query.filter(or_(User.name != 'wang',User.email.endswith('163.com') )).all()

8、查询名字和邮箱都以 li 开头的所有数据

    User.query.filter(User.name.startswith("li"), User.email.startswith("li")).all()
    User.query.filter(and_(User.name.startswith("li"), User.email.startswith("li"))).all()

9、查询password是 123456 或者 emailneuedu.com 结尾的所有数据

    User.query.filter(or_(User.password == '123456', User.email.endswith('neuedu.com'))).all()

10、查询id为 [1, 3, 5, 7, 9] 的用户列表

    User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()

11、 查询name为liu的角色数据

     User.query.filter(User.name == 'liu').all()

12、查询所有用户数据,并以邮箱排序

    User.query.order_by(User.email.desc()).all()

13、每页3个,查询第2页的数据

    pn = User.query.paginate(2, 3)
    pn.items # 获取该页内容
    pn.page # 获取该页页码
    pn.pages # 获取总页数

14、定义模型,并显示作者名和书名
作者Author(),表名 author,属性有:id,name,au_book(外键)
书名Book(),表名 books ,属性有:id,name,au_book

class Author(db.Model):
    __tablename__ = 'author'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    au_book = db.Column(db.Integer, db.ForeignKey('book.id'))

    def __repr__(self):
        return 'Author:{}'.format(self.name)


class Book(db.Model):
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    au_book = db.relationship('Book', backref='book')

    def __repr__(self):
        return 'Book:{}'.format(self.name)

IX、增删改的操作

Flask-SQLAlchemy的增删改操作可以理解为是通过与数据库建立一个小型会话的方式进行的,下面介绍一下这些基于db = SQLAlchemy(app)的操作

操作 说明
create_all() 建表操作,db.create_all()
drop_all() 删除表操作,db.drop_all()
add_all() 插入操作,db.seesion.add_all([]),插入可选择使用list进行批量插入
commit() 提交操作,db.session.commit(),提交其语句以上的所有更改数据库内容的操作
delete() 删除操作,db.session.delete(),删除一条数据

更新操作只需要选择需要更新的对象,修改其属性值然后提交即可。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342