使用SQLAlchemy ORM有效地更新数据库

我正在开始一个新的应用程序,并着眼于使用ORM – 特别是SQLAlchemy。

假设我在数据库中有一个'foo'列,我想增加它。 在sqlite中,这很简单:

db = sqlite3.connect('mydata.sqlitedb') cur = db.cursor() cur.execute('update table stuff set foo = foo + 1') 

我想出了SQLAlchemy的SQL构build器等价物:

 engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb') md = sqlalchemy.MetaData(engine) table = sqlalchemy.Table('stuff', md, autoload=True) upd = table.update(values={table.c.foo:table.c.foo+1}) engine.execute(upd) 

这稍微慢一些,但是没有太多。

这是我对SQLAlchemy ORM方法的最佳猜测:

 # snip definition of Stuff class made using declarative_base # snip creation of session object for c in session.query(Stuff): c.foo = c.foo + 1 session.flush() session.commit() 

这样做是正确的,但只要其他两种方法只需要五十倍。 我认为这是因为它必须把所有的数据存入内存才能使用它。

有没有什么办法可以使用SQLAlchemy的ORM生成高效的SQL? 或者使用任何其他的Python ORM? 或者我应该回去用手写SQL?

SQLAlchemy的ORM旨在与SQL层一起使用,而不是隐藏它。 但在同一事务中使用ORM和普通SQL时,必须记住一两个事项。 基本上,从一方面来说,当你从会话中清除更改时,ORM数据修改只会触发数据库。 另一方面,SQL数据操作语句不会影响会话中的对象。

所以如果你说

 for c in session.query(Stuff).all(): c.foo = c.foo+1 session.commit() 

它会按照所说的去做,从数据库中获取所有对象,修改所有的对象,然后在需要刷新对数据库的更改时,逐一更新行。

相反,你应该这样做:

 session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1})) session.commit() 

这将作为您所期望的一个查询执行,并且由于至less默认的会话configuration在提交时会话中的所有数据都将失效,因此您没有任何陈旧的数据问题。

在几乎发布的0.5系列中,你也可以使用这个方法来更新:

 session.query(Stuff).update({Stuff.foo: Stuff.foo + 1}) session.commit() 

这将基本上运行与前面的代码片段相同的SQL语句,但也select更改的行,并过期会话中的任何陈旧的数据。 如果您知道在更新之后没有使用任何会话数据,则还可以将update_session = False添加到update语句中,并删除该select。

 session.query(Clients).filter(Clients.id == client_id_list).update({'status': status}) session.commit() 

试试这个=)

有几种方法可以使用sqlalchemy进行更新

 1) for c in session.query(Stuff).all(): c.foo += 1 session.commit() 2) session.query().\ update({"foo": (Stuff.foo + 1)}) session.commit() 3) conn = engine.connect() stmt = Stuff.update().\ values(Stuff.foo = (Stuff.foo + 1)) conn.execute(stmt) 

经过testing,我会尝试:

 for c in session.query(Stuff).all(): c.foo = c.foo+1 session.commit() 

(IIRC,commit()没有flush())。

我发现有时做一个大的查询,然后在python中迭代可以比许多查询快两个数量级。 我假设迭代查询对象的效率低于迭代查询对象的all()方法生成的列表的效率。

[请注意下面的评论 – 这并没有加速一切]。

如果是因为在创build对象方面的开销,那么它可能无法用SA加速。

如果是因为它正在加载相关的对象,那么你可能可以用延迟加载来做一些事情。 有很多的对象是由于引用而创build的吗? (IE,获取一个公司对象也得到所有相关的People对象)。

下面是一个如何解决同样的问题,而不必手动映射字段的例子:

 from sqlalchemy import Column, ForeignKey, Integer, String, Date, DateTime, text, create_engine from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.orm.attributes import InstrumentedAttribute engine = create_engine('postgres://postgres@localhost:5432/database') session = sessionmaker() session.configure(bind=engine) Base = declarative_base() class Media(Base): __tablename__ = 'media' id = Column(Integer, primary_key=True) title = Column(String, nullable=False) slug = Column(String, nullable=False) type = Column(String, nullable=False) def update(self): s = session() mapped_values = {} for item in Media.__dict__.iteritems(): field_name = item[0] field_type = item[1] is_column = isinstance(field_type, InstrumentedAttribute) if is_column: mapped_values[field_name] = getattr(self, field_name) s.query(Media).filter(Media.id == self.id).update(mapped_values) s.commit() 

所以要更新Media实例,你可以这样做:

 media = Media(id=123, title="Titular Line", slug="titular-line", type="movie") media.update()