我们使用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. 对于MVVM的理解

    MVVM 是Model-View-ViewModel的缩写. Model 代表数据模型,也可以在model中定义数据修改和操作的业务逻辑. View 代表UI组件,负责姜黄素局模型转化成UI展现出来. ...

  2. Tensorflow 从文件中载入训练数据

    本节包含: 用纯文本文件准备训练数据 加载文件中的训练数据 一.用纯文本文件准备训练数据 1.数据的数字化 比如,“是” —— “1”,“否” —— “0” “优”,“中”,“差” —— 1 2 3  ...

  3. HNU_团队项目_出现的Error总结_1

    今天开始记录开发中的Error,实时更新,以10条为一个博客,会给出相应的错误截图和解决方法.数据库框架Mybatis的配置和使用,详见之后发布的相关博客. 之后会对每一个错误进行分析,单独成一篇随笔 ...

  4. Python爬虫学习==>第八章:Requests库详解

    学习目的: request库比urllib库使用更加简洁,且更方便. 正式步骤 Step1:什么是requests requests是用Python语言编写,基于urllib,采用Apache2 Li ...

  5. cisco 访问控制列表

    LAB-A:Lab-A(config)#host Lab-A Lab-A(config)#interface Ethernet0/0  LAB-A(config-if)#ip address 172. ...

  6. 应用安全 - JavaScript - 框架 - Jquery - 漏洞 - 汇总

    jQuery CVE-2019-11358 Date 类型 原型污染 影响范围 CVE-2015-9251  Date 类型跨站 影响范围<jQuery 3.0.0

  7. 【Linux开发】linux设备驱动归纳总结(六):3.中断的上半部和下半部——工作队列

    linux设备驱动归纳总结(六):3.中断的上半部和下半部--工作队列 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. Sink - 汇聚点

    !!!1.Logger Sink 记录INFO级别的日志,通常用于调试.   属性说明: !channel –   !type – The component type name, needs to ...

  9. (转)shell脚本使用curl获取访问网站的状态码

    curl -I -m 10 -o /dev/null -s -w %{http_code} www.baidu.com -I 仅测试HTTP头-m 10 最多查询10s-o /dev/null 屏蔽原 ...

  10. ORACLE数据库备份与恢复详解

    ORACLE数据库备份与恢复详解 学习过程中的总结,有兴趣不妨看看,如果有不对的地方,高手不要留情!! Oracle的备份与恢复有三种标准的模式,大致分为两 大类,备份恢复(物理上的)以及导入导出(逻 ...