我们使用Flask 0.11.1,Flask-SQLAlchemy 2.1使用PostgreSQL作为DBMS.

示例使用以下代码更新数据库中的数据:

entry = Entry.query.get(1)
entry.name = 'New name'
db.session.commit()

从Flask shell执行时,这完全正常,因此数据库已正确配置.现在,我们的控制器用于更新条目,略微简化(没有验证和其他样板),如下所示:

def details(id):
entry = Entry.query.get(id) if entry:
if request.method == 'POST':
form = request.form
entry.name = form['name']
db.session.commit()
flash('Updated successfully.')
return render_template('/entry/details.html', entry=entry)
else:
flash('Entry not found.')
return redirect(url_for('entry_list')) # In the application the URLs are built dynamically, hence why this instead of @app.route
app.add_url_rule('/entry/details/<int:id>', 'entry_details', details, methods=['GET', 'POST'])

当我在details.html中提交表单时,我可以完全看到更改,这意味着表单已正确提交,有效并且模型对象已更新.但是,当我重新加载页面时,更改已经消失,就好像它已被DBMS回滚一样.

我启用了app.config [‘SQLALCHEMY_ECHO’] = True,我可以在自己的手动提交之前看到“ROLLBACK”.

如果我换行:

entry = Entry.query.get(id)

至:

entry = db.session.query(Entry).get(id)

正如https://stackoverflow.com/a/21806294/4454028中所解释的那样,它确实按预期工作,因此我猜测Flask-SQLAlchemy的Model.query实现中存在某种错误.

但是,由于我更喜欢​​第一个构造,我对Flask-SQLAlchemy进行了快速修改,并从原始版本重新定义了查询@property:

class _QueryProperty(object):

    def __init__(self, sa):
self.sa = sa def __get__(self, obj, type):
try:
mapper = orm.class_mapper(type)
if mapper:
return type.query_class(mapper, session=self.sa.session())
except UnmappedClassError:
return None

至:

class _QueryProperty(object):

    def __init__(self, sa):
self.sa = sa def __get__(self, obj, type):
return self.sa.session.query(type)

其中sa是Flask-SQLAlchemy对象(即控制器中的db).

现在,这就是事情变得奇怪的地方:它仍然没有保存变化.代码完全相同,但DBMS仍在回滚我的更改.

我读到Flask-SQLAlchemy可以在拆卸时执行提交,并尝试添加以下内容:

app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

突然间,一切正常.问题是:为什么?

是否应该只在视图完成渲染时才会发生拆解?为什么修改后的Entry.query与db.session.query(Entry)的行为不同,即使代码相同?

最佳答案
以下是更改模型实例并将其提交到数据库的正确方法:

# get an instance of the 'Entry' model
entry = Entry.query.get(1) # change the attribute of the instance; here the 'name' attribute is changed
entry.name = 'New name' # now, commit your changes to the database; this will flush all changes
# in the current session to the database
db.session.commit()

注意:不要使用SQLALCHEMY_COMMIT_ON_TEARDOWN,因为它被认为是有害的,也从文档中删除.见the changelog for version 2.0.

编辑:如果你有两个普通会话对象(使用sessionmaker()创建)而不是作用域会话,那么在上面调用db.session.add(entry)代码会引发错误sqlalchemy.exc.InvalidRequestError:对象”已经附加会话’2′(这是’3′).有关sqlalchemy会话的更多信息,请阅读以下部分

范围会话与正常会话的主要区别

我们主要从sessionmaker()调用构造并用于与我们的数据库通信的会话对象是正常会话.如果再次调用sessionmaker(),您将获得一个新的会话对象,其状态独立于上一个会话.例如,假设我们有两个以下列方式构造的会话对象:

from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String) from sqlalchemy import create_engine
engine = create_engine('sqlite:///') from sqlalchemy.orm import sessionmaker
session = sessionmaker()
session.configure(bind=engine)
Base.metadata.create_all(engine) # Construct the first session object
s1 = session()
# Construct the second session object
s2 = session()

然后,我们将无法同时向s1和s2添加相同的User对象.换句话说,一个对象最多只能附加一个唯一的会话对象.

>>> jessica = User(name='Jessica')
>>> s1.add(jessica)
>>> s2.add(jessica)
Traceback (most recent call last):
......
sqlalchemy.exc.InvalidRequestError: Object '' is already attached to session '2' (this is '3')

但是,如果从scoped_session对象检索会话对象,那么我们就没有这样的问题,因为scoped_session对象维护了同一会话对象的注册表.

>>> session_factory = sessionmaker(bind=engine)
>>> session = scoped_session(session_factory)
>>> s1 = session()
>>> s2 = session()
>>> jessica = User(name='Jessica')
>>> s1.add(jessica)
>>> s2.add(jessica)
>>> s1 is s2
True
>>> s1.commit()
>>> s2.query(User).filter(User.name == 'Jessica').one()

请注意,s1和s2是相同的会话对象,因为它们都是从保持对同一会话对象的引用的scoped_session对象中检索的.

提示

因此,尽量避免创建多个普通会话对象.创建会话的一个对象,并在从声明模型到查询的任何地方使用它.

补充:

也存在使用db.session.commit()提交数据后,使用model.query()查不到新增加的数据

原因:db.session.commit()是提交了数据到数据库,但是没有刷新模型映射中的数据,也就是model.query()中的数据。
而使用db.session.query()则是 从整个服务会话中进行查询,而db.session.commit()提交的数据在这里是有刷新的,根源还是上面提到的范围会话与正常会话的主要区别。

SQLAlchemy中Model.query和session.query(Model)的区别的更多相关文章

  1. SqlAlchemy 中操作数据库时session和scoped_session的区别(源码分析)

    原生session: from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from sqlalch ...

  2. flask 源码专题(五):SqlAlchemy 中操作数据库时session和scoped_session的区别

    1原生session: from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from sqlalc ...

  3. 灵活使用 SQLAlchemy 中的 ORM 查询

    之前做查询一直觉得直接拼 SQL 比较方便,用了 SQLAlchemy 的 ORM 查询之后,发现也还可以,还提高了可读性. 这篇文章主要说说 SQLAlchemy 常用的 ORM 查询方式,偏实践. ...

  4. springMVC 返回类型选择 以及 SpringMVC中model,modelMap.request,session取值顺序

    springMVC 返回类型选择 以及 SpringMVC中model,modelMap.request,session取值顺序 http://www.360doc.com/content/14/03 ...

  5. spring学习之springMVC 返回类型选择 以及 SpringMVC中model,modelMap.request,session取值顺序

    spring mvc处理方法支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void.下面将对具体的一一进行说明: ModelAn ...

  6. SQLAlchemy 中的 Session、sessionmaker、scoped_session

    目录 一.关于 Session 1. Session是缓存吗? 2. Session作用: 3. Session生命周期: 4. Session什么时候创建,提交,关闭? 4. 获取一个Session ...

  7. Thinkphp3.2.3 执行query命令 包括在模板中使用<php> </php>时 query的使用方法

    $sql="select * from `rjshop_productbase` where `id`=1"; $Model =M();$query=$Model->quer ...

  8. SQLAlchemy ORM教程之二:Query

    from:https://www.jianshu.com/p/8d085e2f2657 这是继SQLAlchemy ORM教程之一:Create后的第二篇教程.在上一篇中我们主要是解决了如何配置ORM ...

  9. springmvc session和model解析

    关于springMVC中的session,有2种使用方法,第一种是直接传递httpsession,第二种是使用@SessionAttributes("userId") 注解 这里附 ...

随机推荐

  1. Sqlserver实现故障转移 — sqlserver镜像备份实现故障转移(3)

    目的:在已经加域的计算机上安装sqlserver2012,并配置数据库镜像实时同步,并实现故障转移. 在数据库层面实现故障自动转移后,应用程序里改怎么写数据库连接呢?其实使用ADO.NET或者SQL ...

  2. NOIp2013D2T3 华容道【搜索&图论-最短路】

    题目传送门 暴力搜索 看到这道题的第一反应就是直接上$bfs$啦,也没有想到什么更加优秀的算法. 然后就是$15$分钟打了$70$分,有点震惊,纯暴力诶,这么多白给分嘛,太划算了,这可是$D2T3$诶 ...

  3. C++学习笔记-C++与C语言的一些区别

    本文主要是整理一些C++与C的一些小的区别,也就是在使用C与C++时候需要注意的一些问题,C++是以C语言为基础的,并且完全兼容C语言的特性 注释 C语言的注释形式为 /* 注释内容 */ 而C++提 ...

  4. luoguP1886 滑动窗口(单调队列模板题)

    题目链接:https://www.luogu.org/problem/P1886#submit 题意:给定n个数,求大小为k的滑动窗口中最小值和最大值. 思路:单调队列模板题. AC代码: #incl ...

  5. [Python3 练习] 011 利用异常解题

    目录 函数式编程 Functional Programming 1. 简介 2. 函数 3. 匿名函数 3.1 lambda 表达式也称"匿名函数" 3.2 lambda 表达式的 ...

  6. sql server查询数据库连接数

    设置最大连接数 下面的T-SQL 语句可以配置SQL Server 允许的并发用户连接的最大数目. exec sp_configure 'show advanced options', 1exec s ...

  7. 厉害了,ES 如何做到几十亿数据检索 3 秒返回!

    一.前言 数据平台已迭代三个版本,从头开始遇到很多常见的难题,终于有片段时间整理一些已完善的文档,在此分享以供所需朋友的 实现参考,少走些弯路,在此篇幅中偏重于ES的优化,关于HBase,Hadoop ...

  8. JAVA基础:接口

    接口声明: interface 接口名 实现接口: class 类名 implements 接口名 接口特点: 1. 接口中的成员变量默认都是public.static.final类型的,必须被显式初 ...

  9. python多进程,并获取每个进程的返回值

    pool = multiprocessing.Pool(processes=10) row = [...] for row in rows: task_id = row[1] img_id = row ...

  10. postgres 常规操作杂记

    分布式:1.扩容不方便(数据重分布)2.分布键变更很麻烦3.分布键选择(架构设计)谨慎4.跨库join性能差5.分布式事务性能差6.sql限制多,功能确实多7.应用改造成本巨大8.全局一致性时间点恢复 ...