用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog
目录
前文列表
用 Flask 来写个轻博客 (1) — 创建项目
用 Flask 来写个轻博客 (2) — Hello World!
用 Flask 来写个轻博客 (3) — (M)VC_连接 MySQL 和 SQLAlchemy
用 Flask 来写个轻博客 (4) — (M)VC_创建数据模型和表
扩展阅读
SQLAlchemy 的 CRUD
CRUD 提供了在 Web 应用程序中所需要的所有操作和检视数据的基础功能, 尤其在 REST 风格的应用中, CRUD 就能实现一切所需功能.
本篇博文主要记录 SQLAlchemy 实现 CRUD 的语句, 依然是在 manager shell 中完成:
(blog)fanguiju@fanguiju:/opt/JmilkFan-s-Blog$ python manage.py shell
>>> app
<Flask 'main'>
>>> User
<class 'models.User'>
>>> db
<SQLAlchemy engine='mysql+pymysql://root:fanguiju@127.0.0.1:3306/myblog?charset=utf8'>
为了让在 manager shell 中的执行效果更直观一些, 首先, 先对 User models 做一些修改:
from flask.ext.sqlalchemy import SQLAlchemy
from main import app
# INIT the sqlalchemy object
# Will be load the SQLALCHEMY_DATABASE_URL from config.py
db = SQLAlchemy(app)
class User(db.Model):
"""Represents Proected users."""
# Set the name for table
__tablename__ = 'users'
id = db.Column(db.String(45), primary_key=True)
username = db.Column(db.String(255))
password = db.Column(db.String(255))
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
def __repr__(self):
"""Define the string format for instance of User."""
return "<Model User `{}`>".format(self.username)
Create 增添数据
为新建的 User models 添加一条记录的操作看起来跟 Git 的提交操作非常类似, 其包含的意义也大致相同, 是为了尽量减少不必要的 I/O 操作:
- add: 把数据添加到会话对象中 (数据状态为待保存)
- commit: 将会话对象中的数据提交 (数据被写入数据库中)
>>> from uuid import uuid4
>>> user = User(id=str(uuid4()), username='jmilkfan', password='fanguiju')
>>> db.session.add(user)
>>> db.session.commit()
然后再查看一下数据库, 检验是否生效:
mysql> select * from users;
+--------------------------------------+----------+----------+
| id | username | password |
+--------------------------------------+----------+----------+
| d2c3a206-c4d4-4ce9-91f1-ed4bbbb28c76 | jmilkfan | fanguiju |
+--------------------------------------+----------+----------+
1 row in set (0.00 sec)
从这个例子可以看出, 一个 class User 的实例化对象就是一条包含了字段值的记录对象, 将该记录对象添加并提交到 session , 就会把记录对象的数据写入到数据库中.
Retrieve 读取数据
把数据添加仅数据库表后, SQLAlchemy 可以通过 Model.query 方法对数据进行查询. Model.query == db.session.query(Model)
两种写法是等效的. 区别在于前者使用的是 flask_sqlalchemy.BaseQuery object, 后者使用的是 sqlalchemy.orm.query.Query object . 但两者本质上都是一个 Query 对象.
读取数据有两种情况:
- 读取一条数据: 需要指定唯一的过滤条件来获取, 一般会使用主键作为过滤条件.
>>> user = User.query.first()
>>> user.username
u'fanguiju'
# 返回表中的第一条记录
# 其中 User.query 返回的是 flask_sqlalchemy.BaseQuery object
# flask_sqlalchemy.BaseQuery object 拥有对数据库操作的所有抽像方法
# or
>>> user = User.query.get('49f86ede-f1e5-410e-b564-27a97e12560c')
>>> user
<Model User `fanguiju`>
# 返回表中指定主键的一条记录
# or
>>> user = db.session.query(User).filter_by(id='49f86ede-f1e5-410e-b564-27a97e12560c').first()
>>> user
<Model User `fanguiju`>
# 返回符合过滤条件的第一条记录
# 其中 db.session.query(User).filter_by(id='49f86ede-f1e5-410e-b564-27a97e12560c') 返回的是一个 sqlalchemy.orm.query.Query object 对象
# sqlalchemy.orm.query.Query.first() 才是一个 User 对象
- 读取多条数据: 指定任意条件作为过滤条件或者不过滤的获取全部数据
# 获取多条记录
>>> user = db.session.query(User).filter_by(username='fanguiju').all()
>>> user
[<Model User `fanguiju`>]
# 返回符合过滤条件的所有记录, 将所有 username == fanguiju 的记录都获取
# 获取全部数据
>>> users = User.query.all()
>>> users
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
# or
>>> db.session.query(User).all()
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
NOTE: 因为在 Flask 中的 SQLAlchemy 拥有 flask-sqlalchemy 和 sqlalchemy.orm 两种语法, 为了避免看花眼的情况, 以后的代码中只会使用通用性更强一些的 sqlalchemy.orm 的语法.
除了上述两种操作之外, 还有非常之多不同花样的数据读取方式, 读取数据是 4 种操作类型中最复杂多样的一中操作类型.
限制返回记录的数目
这个返回特征常与数据的分页功能结合使用.
>>> users = db.session.query(User).limit(10).all()
返回记录的排序
SQLAlchemy 默认会根据主键的顺序来排序, 也是要显示的使用 order_by 函数来指定排序条件和排序的方式:
# 正向排序
>>> users = db.session.query(User).order_by(User.username).all()
>>> users
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
# 反向排序
>>> users = db.session.query(User).order_by(User.username.desc()).all()
>>> users
[<Model User `jmilkfan`>, <Model User `fanguiju`>]
>>> users = db.session.query(User).order_by(User.password).all()
>>> users
[<Model User `jmilkfan`>, <Model User `fanguiju`>]
查询函数的链式调用
Query 对象提供了非常多且灵活的数据库操作抽象方法, 我们可以链式的去组合这些方法来达到希望的效果. 但需要注意的是, 删除操作的链式调用一定要谨慎而为.
>>> users = db.session.query(User).order_by(User.username).limit(10).all()
可以使用 dir() 内置函数来查看一个 Query 对象提供的方法列表.
dir(db.session.query(User))
NOTE: 一条读取语句的链式操作都是一个 first() 或 all() 函数结束的. 它们会终止链式调用并返回结果.
Flask-SQLAlchemy 的专有分页函数 pagination
pagination(): 是专门设计来实现分页功能的函数, 所以必须由 flask_sqlalchemy.BaseQuery object 来调用.
>>> db.session.query(User).paginate(1,1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Query' object has no attribute 'paginate'
- 第一个参数表示查询返回第几页的内容
- 第二个参数表示每页显示的对象数量
>>> User.query.paginate(1,10) # 查询第 1 页,且 1 页显示 10 条内容
<flask_sqlalchemy.Pagination object at 0x7f214419fe50>
paginate() 与 first()/all() 不同, 后者返回的是一个 models 对象或 models 对象列表, 而前者返回的是一个 pagination 对象. 而且 pagination 对象还包含了几个特有的属性:
>>> user_page = User.query.paginate(1, 10)
# 获取这一页所包含的数据对象
>>> user_page.items
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
# 获取这一页的页码
>>> user_page.page
1
# 获取总共的页数
>>> user_page.pages
1
# 是否有上一页
>>> user_page.has_prev
False
# 如果有上一页的话, 获取上一页的 pagination 对象
>>> if user_page.has_prev:
... user_page.prev()
...
# 是否有下一页
>>> user_page.has_next
False
# 如果有下一页的话, 获取下一个的 pagination 对象
>>> if user_page.has_next:
... user_page.next()
...
Query 的过滤器
在查询数据时, 可以根据一定的条件集合来获得过滤后的数据. SQLAlchemy 提供了过滤器 query.filter_by()
和 query.filter()
, 过滤器接受的参数就是过滤条件, 有下面几种形式:
- 字段键值对, EG.
username='fanguiju'
- 比较表达式, EG.
User.id > 100
- 逻辑函数, EG.
in_/not_/or_
EXAMPLE:
>>> user = db.session.query(User).filter(User.username.in_(['fanguiju', 'jmilkfan'])).limit(1).all() # 当然也可以结合链式函数来使用
>>> user
[<Model User `fanguiju`>]
>>> user = db.session.query(User).filter(not_(User.password == None)).all()
>>> user
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
>>> user = db.session.query(User).filter(or_(not_(User.username == None), User.password != None)).all()
>>> user
[<Model User `fanguiju`>, <Model User `jmilkfan`>]
可以将 query.filter()
内置的逻辑函数 in_/not_/or_
结合使用来实现更复杂的过滤.
Update 更新数据
>>> user = db.session.query(User).first()
>>> user.username
u'fanguiju'
>>> user = db.session.query(User).update({'username': 'update_fanguiju'})
>>> db.session.commit()
>>> user = db.session.query(User).first()
>>> user.username
u'update_fanguiju'
如上述例子, 先定位到你希望更新的记录, 然后通过 Query 对象的 update()
传递要更新内容. 注意: 更新的内容必须是 Dict 数据类型.
需要注意的是: 就如使用原生 SQL 指令来更新记录一样, 如果没有指定要更新具体的哪一条记录的话, 会将该字段所在列的所有记录值一同更新, 所以切记使用过滤条件来定位到具体需要更新的记录.
而且 update()
会自动的添加 User 的实例化对象到 session 中, 所以直接 commit 就可以写入到数据库了.
Delete 删除数据
>>> user = db.session.query(User).first()
>>> user
<Model User `update_fanguiju`>
>>> db.session.delete(user)
>>> db.session.commit()
将查询返回的 User 实例化对象进行 session 的 delete 操作, 就能够删除该对象所映射的记录数据了.
用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解的更多相关文章
- 用 Flask 来写个轻博客
用 Flask 来写个轻博客 用 Flask 来写个轻博客 (1) — 创建项目 用 Flask 来写个轻博客 (2) — Hello World! 用 Flask 来写个轻博客 (3) — (M)V ...
- 用 Flask 来写个轻博客 (37) — 在 Github 上为第一阶段的版本打 Tag
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 第一阶段结语 打 Tag 前文列表 用 Flask 来写个轻博客 (1 ...
- 用 Flask 来写个轻博客 (36) — 使用 Flask-RESTful 来构建 RESTful API 之五
目录 目录 前文列表 PUT 请求 DELETE 请求 测试 对一条已经存在的 posts 记录进行 update 操作 删除一条记录 前文列表 用 Flask 来写个轻博客 (1) - 创建项目 用 ...
- 用 Flask 来写个轻博客 (35) — 使用 Flask-RESTful 来构建 RESTful API 之四
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 POST 请求 身份认证 测试 前文列表 用 Flask 来写个轻博客 ...
- 用 Flask 来写个轻博客 (34) — 使用 Flask-RESTful 来构建 RESTful API 之三
目录 目录 前文列表 应用请求中的参数实现 API 分页 测试 前文列表 用 Flask 来写个轻博客 (1) - 创建项目 用 Flask 来写个轻博客 (2) - Hello World! 用 F ...
- 用 Flask 来写个轻博客 (33) — 使用 Flask-RESTful 来构建 RESTful API 之二
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 构建 RESTful Flask API 定义资源路由 格式 ...
- 用 Flask 来写个轻博客 (32) — 使用 Flask-RESTful 来构建 RESTful API 之一
目录 目录 前文列表 扩展阅读 RESTful API REST 原则 无状态原则 面向资源 RESTful API 的优势 REST 约束 前文列表 用 Flask 来写个轻博客 (1) - 创建项 ...
- 用 Flask 来写个轻博客 (31) — 使用 Flask-Admin 实现 FileSystem 管理
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 编写 FileSystem Admin 页面 Flask-A ...
- 用 Flask 来写个轻博客 (30) — 使用 Flask-Admin 增强文章管理功能
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 实现文章管理功能 实现效果 前文列表 用 Flask 来写个 ...
随机推荐
- 109、TensorFlow计算张量的值
# 当计算图创建成功时 # 你就可以运行这个计算图,然后生成一个新的张量 # 并且得到这个张量指向的计算图中具体的数值 #这个功能在debug的时候非常有必要 #最简单获得张量具体值的方法是使用Ten ...
- linux to extract contents between patterns
参考:http://stackoverflow.com/questions/19177721/extract-lines-between-two-patterns-from-a-lfile awk ' ...
- T1218:取石子游戏
[题目描述] 有两堆石子,两个人轮流去取.每次取的时候,只能从较多的那堆石子里取,并且取的数目必须是较少的那堆石子数目的整数倍,最后谁能够把一堆石子取空谁就算赢. 比如初始的时候两堆石子的数目是25和 ...
- 【转载 | 翻译】Visualizing A Neural Machine Translation Model(神经机器翻译模型NMT的可视化)
转载并翻译Jay Alammar的一篇博文:Visualizing A Neural Machine Translation Model (Mechanics of Seq2seq Models Wi ...
- cesium加载gltf模型
cesium加载gltf模型 一.采用vue-cesium:在项目里加载依赖包.命令如下: npm i --save vue-cesium 在main.js中加入如下代码: https://www.n ...
- python基础----斐波那契数列
python实现斐波那契数列的三种方法 """ 斐波那契数列 0,1,1,2,3,5,8,13,21,... """ # 方法一:while ...
- android 查看网络图片
public class MainActivity extends Activity { private EditText pathText; private ImageView imageView; ...
- java中垃圾收集的方法有哪些?
java中垃圾收集的方法有哪些? 一.引用计数算法(Reference Counting) 介绍:给对象添加一个引用计数器,每当一个地方引用它时,数据器加1:当引用失效时,计数器减1:计数器为0的即可 ...
- Web前端基础学习-3
bfc(block formatting context) 块级格式化上下文 生成bfc的方式: 1.根元素: 2.float属性不为none(脱离文档流): 3.position为absolute或 ...
- Android No static field XXX of type I in class Lcom/XXX/R$id错
问题复现: 问题原因: 出现这样的情况,你先检查你的依赖工程(module)的对应布局layout/xxx.xml是否跟主项目的layout重名,你点开R文件的时候,你会发现你的布局发生了错乱,导致你 ...