我们使用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. CentOS的SVN服务器搭建与自动部署全过程

    CentOS的SVN服务器搭建与自动部署全过程 http://www.jb51.net/article/106218.htm authz-db = authz 引起的 svn 认证失败 http:// ...

  2. Window Position

    IE, Safari, Opera, and Chrome all provide screenLeft and screenTop properties that indicate the wind ...

  3. 前端vscode常用插件

    Auto Rename Tag 这是一个html标签的插件,可以让你修改一边标签,另外一边自动改变. Beautify 格式化代码插件 Braket Pair Colorizer 给js文件中的每一个 ...

  4. Windows下安装jdk-12.0.2

    Java更新到12,新版本的Java中不再存在jre,配置环境变量有所改变. 下载最新jdk 下载地址:https://www.oracle.com/technetwork/java/javase/d ...

  5. nginx reload的原理

    nginx启动时,会启动两个进程: 一个是Master进程和worker进程.改变配置后nginx做的事1)改变了nginx配置之后,HUP signal的信号需要发送给主进程.2)主进程首先会检测新 ...

  6. zabbix支持的主要监控方式

    一.zabbix支持的主要监控方式: zabbix主要Agent,Trapper,SNMP,JMX,IPMI这几种监控方式,本文章主要通过监控理论和实际操作测试等方式来简单介绍这几种方式的监控原理和优 ...

  7. EM算法分析

    参考来源: <机器学习>——周志华 https://www.cnblogs.com/jerrylead/archive/2011/04/06/2006936.html 几个概念 极大似然估 ...

  8. Tensorflow实战第十课(RNN MNIST分类)

    设置RNN的参数 我们本节采用RNN来进行分类的训练(classifiction).会继续使用手写数据集MNIST. 让RNN从每张图片的第一行像素读到最后一行,然后进行分类判断.接下来我们导入MNI ...

  9. 出现错误lambda expressions are not supported at language level 7解决方法

    AS工程出现题目中的错误.解决办法: 这两个选中为1.8. 然后AS会自动在app的build.gradle下面,android{}里面增加 同时会修改app.iml文件中的如下部分,将Languag ...

  10. nslookup 命令

    NAME nslookup - query Internet name servers interactively SYNOPSIS nslookup [-option] [name | -] [se ...