SQLAlchemy 0.5有什么新功能?
关于此文档
本文档描述了上次发布于2008年10月12日的SQLAlchemy版本0.4和上次发布于2010年1月16日的SQLAlchemy版本0.5之间的更改。
文件日期:2009年8月4日
本指南记录了影响用户将其应用程序从0.4系列SQLAlChemy迁移到0.5的API更改。也推荐给那些从 Essential SQLAlchemy ,它只涵盖了0.4,甚至似乎有一些古老的0.3主义在里面。请注意,SQLAlChemy 0.5删除了在0.4系列的整个过程中不推荐使用的许多行为,并且还不推荐使用更多特定于0.4的行为。
主要文件变更
文档的某些部分已经完全重写,可以作为新ORM特性的介绍。这个 Query
和 Session
特别是对象在API和行为上有一些明显的差异,这从根本上改变了许多基本的方法,特别是在构建高度定制的ORM查询和处理陈旧的会话状态、提交和回滚方面。
折旧来源
另一个信息源记录在一系列单元测试中,说明一些常见的最新用法。 Query
模式;此文件可在 [source:sqlalchemy/trunk/test/orm/test_deprecations.py] .
需求变更
需要python 2.4或更高版本。sqlAlchemy 0.4行是支持python 2.3的最后一个版本。
对象关系映射
查询中的列级表达式。 -详见 tutorial ,
Query
能够创建特定的SELECT语句,而不仅仅是针对整行的语句:session.query(User.name, func.count(Address.id).label("numaddresses")).join(Address).group_by(User.name)
任何多列/实体查询返回的元组为 已命名 元组:
for row in session.query(User.name, func.count(Address.id).label('numaddresses')).join(Address).group_by(User.name): print("name", row.name, "number", row.numaddresses)
Query
有一个statement
存取器,以及subquery()
允许的方法Query
用于创建更复杂的组合:subq = session.query(Keyword.id.label('keyword_id')).filter(Keyword.name.in_(['beans', 'carrots'])).subquery() recipes = session.query(Recipe).filter(exists(). where(Recipe.id==recipe_keywords.c.recipe_id). where(recipe_keywords.c.keyword_id==subq.c.keyword_id) )
对于有别名的联接,建议使用显式ORM别名 -
aliased()
函数生成一个类的“别名”,它允许结合ORM查询对别名进行细粒度控制。而表级别名(即table.alias()
)仍然可用,ORM级别的别名保留ORM映射对象的语义,这对于继承映射、选项和其他场景非常重要。例如。:Friend = aliased(Person) session.query(Person, Friend).join((Friend, Person.friends)).all()
query.join()大大增强了。 -现在可以用多种方式为联接指定TARGET和ON子句。当sqla试图通过外键与之形成连接时,可以单独提供一个目标类,方法与
table.join(someothertable)
. 可以提供目标和显式条件,其中条件可以是relation()
名称、实际类描述符或SQL表达式。或者只是一个relation()
名称或类描述符也可以工作。请参阅ORM教程,其中有几个示例。对于不需要(也不喜欢)表和映射器之间抽象的应用程序,建议使用声明性 - [/docs/05/reference/ext/declarative.html Declarative] 模块,用于组合
Table
,mapper()
和用户定义的类对象结合在一起,这是强烈建议的,因为它简化了应用程序配置,确保了“每个类一个映射器”模式,并允许完全范围的配置可用于区分mapper()
电话。分开mapper()
和Table
用法现在被称为“经典的SQLAlchemy用法”,当然可以与声明性自由混合。.c.属性已被删除 从课堂(即
MyClass.c.somecolumn
)与0.4中的情况一样,类级属性可用作查询元素,即Class.c.propname
现在被取代Class.propname
和c
属性继续保留在上Table
对象的名称空间Column
表中存在的对象。要找到映射类的表(如果您没有保留它的话):
table = class_mapper(someclass).mapped_table
遍历列:
for col in table.c: print(col)
使用特定列:
table.c.somecolumn
类绑定描述符支持完整的列运算符集以及文档化的面向关系的运算符,如
has()
,any()
,contains()
等。难以清除的原因
.c.
在0.5中,类绑定描述符具有潜在的不同含义,以及关于类映射的信息,而不是普通的Column
对象-在某些用例中,您特别希望使用其中一个或另一个。通常,使用类绑定描述符会调用一组支持映射/多态性的翻译,而使用表绑定列则不会。在0.4中,这些翻译被广泛应用于所有表达式,但是0.5完全区分了列和映射的描述符,只对后者应用翻译。因此,在许多情况下,尤其是在处理联接表继承配置以及使用时query(<columns>)
,Class.propname
和table.c.colname
不能互换。例如,
session.query(users.c.id, users.c.name)
不同于session.query(User.id, User.name)
在后一种情况下,Query
了解正在使用的映射器以及其他特定于映射器的操作,如query.join(<propname>)
,query.with_parent()
可使用等,但在前一种情况下不能使用。另外,在多态继承场景中,类绑定描述符是指使用中可选择的多态中存在的列,不一定是直接对应于描述符的表列。例如,通过将表继承与person
沿着桌子person_id
每个表的列都将具有Class.person_id
属性映射到person_id
列在person
而不是它们的子类表。0.4版将此行为映射到表绑定Column
对象自动。在0.5中,此自动转换已被删除,因此实际上 can 使用表绑定列作为覆盖多态查询中发生的转换的方法;这允许Query
为了能够在联接的表或具体的表继承设置中创建优化的选择,以及可移植的子查询等。会话现在自动与事务同步。 会话现在在默认情况下自动与事务同步,包括自动刷新和自动过期。事务始终存在,除非使用
autocommit
选择权。当所有三个标志都设置为默认值时,会话在回滚后恢复得很好,很难将过时的数据放入会话中。有关详细信息,请参阅新会话文档。隐式Order By已删除 . 这将影响依赖SA的“隐式排序”行为的ORM用户,该行为表示所有没有
order_by()
将按主映射表的“id”或“oid”列排序,并且所有懒惰/急切加载的集合都将应用类似的排序。在0.5中,必须在上显式配置自动排序mapper()
和relation()
对象(如果需要),或者在使用时Query
.要将0.4映射转换为0.5,从而使其排序行为与0.4或以前的映射极为相似,请使用
order_by
设置在mapper()
和relation()
:mapper(User, users, properties={ 'addresses':relation(Address, order_by=addresses.c.id) }, order_by=users.c.id)
要在backref上设置排序,请使用
backref()
功能:'keywords':relation(Keyword, secondary=item_keywords, order_by=keywords.c.name, backref=backref('items', order_by=items.c.id))
使用声明性?帮助新的
order_by
要求,order_by
现在可以使用稍后在python中评估的字符串来设置friends(这很有效 only 使用声明性的,而不是普通的映射器):class MyClass(MyDeclarativeBase): ... 'addresses':relation("Address", order_by="Address.id")
通常是个好主意
order_by
在relation()s
哪个加载基于列表的项集合,因为该排序不会受到影响。除此之外,最佳做法是Query.order_by()
控制正在加载的主要实体的顺序。Session is now autoflush=True/autoexpire=True/autocommit=False. -要设置,只需打电话
sessionmaker()
没有参数。名字transactional=True
现在是autocommit=False
. 每次发出查询时都会进行刷新(禁用autoflush=False
)commit()
(一如既往),在每个begin_nested()
(因此回滚到保存点是有意义的)。所有对象在每个commit()
之后rollback()
. 回滚之后,挂起的对象将被删除,删除的对象将移回持久对象。这些默认值可以很好地结合在一起,实际上不再需要像clear()
(更名为expunge_all()
同样如此。P.S.:会话现在可以在
rollback()
. 标量和集合属性的更改、添加和删除都将回滚。session.add() replaces session.save(), session.update(), session.save_or_update(). -
session.add(someitem)
和session.add_all([list of items])
方法替代save()
,update()
和save_or_update()
. 在整个0.5过程中,这些方法仍将被弃用。backref配置减少了冗余。 -
backref()
function now uses theprimaryjoin
andsecondaryjoin
arguments of the forwards-facingrelation()
when they are not explicitly stated. It's no longer necessary to specifyprimaryjoin
/secondaryjoin
两个方向分开。简化的多态选项。 -ORM的“多态加载”行为已经简化。在0.4中,mapper()有一个名为
polymorphic_fetch
可以配置为select
或deferred
. 此选项已被删除;映射器现在只会延迟select语句中不存在的任何列。实际使用的select语句由with_polymorphic
映射器参数(也在0.4中,并替换select_table
)以及with_polymorphic()
方法对Query
(也在0.4)。延迟加载继承类的一个改进是,映射器现在在所有情况下都会生成select语句的“优化”版本;也就是说,如果B类从A继承,并且仅存在于B类上的几个属性已过期,则刷新操作将仅在select语句中包含B的表,并且不会联接到A
这个
execute()
方法对Session
将普通字符串转换为text()
构造,以便可以将绑定参数全部指定为“:bindname”,而无需调用text()
明确地。如果此处需要“原始”SQL,请使用session.connection().execute("raw text")
.session.Query().iterate_instances()
已重命名为instances()
. 老年人instances()
返回列表而不是迭代器的方法不再存在。如果你依赖这种行为,你应该使用list(your_query.instances())
.
扩展ORM
在0.5版本中,我们将使用更多的方法来修改和扩展ORM。以下是总结:
映射扩展。 -这是经典的扩展类,它仍然存在。很少需要的方法是
create_instance()
和populate_instance()
. 要在从数据库加载对象时控制对象的初始化,请使用reconstruct_instance()
方法,或者更容易@reconstructor
文档中描述的装饰器。会话扩展。 -这是一个易于使用的会话事件扩展类。特别是,它提供
before_flush()
,after_flush()
和after_flush_postexec()
方法。建议使用MapperExtension.before_XXX
在很多情况下before_flush()
您可以自由地修改会话的刷新计划,这是无法从内部完成的。MapperExtension
.属性扩展。 -这个类现在是公共API的一部分,并且允许拦截属性上的userland事件,包括属性集和删除操作,以及集合附加和删除。它还允许设置或附加值以进行修改。这个
@validates
文档中描述的decorator提供了一种快速的方法,可以将任何映射的属性标记为由特定的类方法“验证”。属性检测自定义。 -API是为彻底替换sqlacalchemy的属性工具,或者在某些情况下只是为了增强它而进行的雄心勃勃的工作而提供的。此API是为Trellis工具包的目的而生成的,但作为公共API提供。以下是一些示例
/examples/custom_attributes
目录。
模式/类型
没有长度的字符串不再生成文本,它将生成varchar -
String
类型不再神奇地转换为Text
指定时键入,但没有长度。这只在发出create table时有效,因为它将发出VARCHAR
没有长度参数,这在许多(但不是全部)数据库上无效。要创建文本(或CLOB,即无边界字符串)列,请使用Text
类型。PickleType() with mutable=True requires an __eq__() method -
PickleType
当mutable=true时,类型需要比较值。比较的方法pickle.dumps()
效率低下且不可靠。如果传入对象未实现__eq__()
也不是None
, thedumps()
使用比较,但发出警告。对于实现的类型__eq__()
其中包括所有字典、列表等,比较将使用==
现在默认情况下是可靠的。convert_bind_param() and convert_result_value() methods of TypeEngine/TypeDecorator are removed. -不幸的是,O'Reilly的书记录了这些方法,尽管它们在0.3之后被否决了。对于用户定义的类型
TypeEngine
, thebind_processor()
和result_processor()
方法应用于绑定/结果处理。任何用户定义的类型,无论扩展TypeEngine
或TypeDecorator
,使用旧的0.3样式可以使用以下适配器轻松适应新样式:class AdaptOldConvertMethods(object): """A mixin which adapts 0.3-style convert_bind_param and convert_result_value methods """ def bind_processor(self, dialect): def convert(value): return self.convert_bind_param(value, dialect) return convert def result_processor(self, dialect): def convert(value): return self.convert_result_value(value, dialect) return convert def convert_result_value(self, value, dialect): return value def convert_bind_param(self, value, dialect): return value
使用上述混音器:
class MyType(AdaptOldConvertMethods, TypeEngine): # ...
这个
quote
旗上Column
和Table
以及quote_schema
旗上Table
现在控制引用正负。默认值为None
也就是说,让规则引用规则生效。什么时候?True
,强制引用。什么时候?False
,引用被强制关闭。柱
DEFAULT
现在可以更方便地使用Column(..., server_default='val')
贬低Column(..., PassiveDefault('val'))
.default=
现在只适用于由python启动的默认值,并且可以与服务器默认值共存。一个新的server_default=FetchedValue()
取代了PassiveDefault('')
用于将列标记为受外部触发器影响并且没有DDL副作用的习惯用法。斯利特
DateTime
,Time
和Date
现在类型 只接受日期时间对象,不接受字符串 作为绑定参数输入。如果您想创建自己的“混合”类型,该类型接受字符串并将结果作为日期对象返回(使用您喜欢的任何格式),请创建一个TypeDecorator
建立在String
. 如果只需要基于字符串的日期,只需使用String
.另外,
DateTime
和Time
当与sqlite一起使用时,类型现在表示python的“微秒”字段。datetime.datetime
对象的方式与str(datetime)
-作为分数秒,不是微秒计数。即:dt = datetime.datetime(2008, 6, 27, 12, 0, 0, 125) # 125 usec # old way '2008-06-27 12:00:00.125' # new way '2008-06-27 12:00:00.000125'
因此,如果现有的基于sqlite文件的数据库打算在0.4和0.5之间使用,您要么升级datetime列以存储新的格式(注意:请对此进行测试,我确信它是正确的):
UPDATE mytable SET somedatecol = substr(somedatecol, 0, 19) || '.' || substr((substr(somedatecol, 21, -1) / 1000000), 3, -1);
或者,启用“传统”模式,如下所示:
from sqlalchemy.databases.sqlite import DateTimeMixin DateTimeMixin.__legacy_microseconds__ = True
默认情况下,连接池不再是线程本地的
0.4有一个不幸的默认设置“pool_threadlocal=true”,当在一个线程中使用多个会话时,会导致意外行为。此标志现在在0.5中关闭。要重新启用0.4的行为,请指定 pool_threadlocal=True
到 create_engine()
或者使用“threadlocal”策略通过 strategy="threadlocal"
.
* 接受ARGs, * args不再接受
政策与 method(\*args)
VS method([args])
是,如果该方法接受表示固定结构的可变长度项集,则需要 \*args
. 如果该方法接受一组可变长度的数据驱动项,则需要 [args]
.
各种query.options()函数
eagerload()
,eagerload_all()
,lazyload()
,contains_eager()
,defer()
,undefer()
全部接受可变长度\*keys
作为他们现在的论点,这允许使用描述符来制定一条路径,即:query.options(eagerload_all(User.orders, Order.items, Item.keywords))
为了向后兼容,仍然接受单个数组参数。
同样,
Query.join()
和Query.outerjoin()
方法接受可变长度 * 参数,为向后兼容接受单个数组:query.join('orders', 'items') query.join(User.orders, Order.items)
这个
in_()
方法只接受列表参数。它不再接受\*args
.
远离的
entity_name -此功能总是有问题,很少使用。0.5更丰富的用例揭示了更多的问题
entity_name
这导致了它的移除。如果单个类需要不同的映射,请将该类拆分成单独的子类,并分别映射它们。这方面的一个示例是在 [wiki:UsageRecipes/EntityName] 。有关基本原理的更多信息,请访问https://groups.google.c om/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d?hl=en。get()/load() cleanup
这个
load()
方法已被删除。它的功能是任意的,基本上是从Hibernate复制的,在那里它也不是一个特别有意义的方法。要获得同等功能:
x = session.query(SomeClass).populate_existing().get(7)
Session.get(cls, id)
和Session.load(cls, id)
已被删除。Session.get()
冗余与session.query(cls).get(id)
.MapperExtension.get()
也被移除(原样MapperExtension.load()
)覆盖的功能Query.get()
,使用子类:class MyQuery(Query): def get(self, ident): # ... session = sessionmaker(query_cls=MyQuery)() ad1 = session.query(Address).get(1)
sqlalchemy.orm.relation()
已删除下列不推荐使用的关键字参数:
foreignkey,association,private,attributeext,is_backref
特别地,
attributeext
替换为extension
-AttributeExtension
类现在位于公共API中。session.Query()
已删除以下不推荐使用的函数:
list,scalar,count_by,select_whereclause,get_by,select_by,join_by,selectfirst,selectone,select,execute,select_statement,select_text,join_to,join_via,selectfirst_by,selectone_by,apply_max,apply_min,apply_avg,apply_sum
另外,
id
关键字参数join()
,outerjoin()
,add_entity()
和add_column()
已删除。目标表别名Query
若要生成列,请使用aliased
构建:from sqlalchemy.orm import aliased address_alias = aliased(Address) print(session.query(User, address_alias).join((address_alias, User.addresses)).all())
sqlalchemy.orm.Mapper
()
get_session()-这个方法不是很明显,但是它有将延迟加载与特定会话关联的效果,即使父对象完全分离,当扩展(如
scoped_session()
还是旧的SessionContextExt
使用。可能一些依赖这种行为的应用程序将不再像预期的那样工作;但是这里更好的编程实践是,如果需要从对象的属性访问数据库,则始终确保对象存在于会话中。
mapper(MyClass, mytable)
映射类不再使用“c”类属性进行检测;例如
MyClass.c
sqlalchemy.orm.collections
“准备”检测的“准备”检测别名已被删除。
sqlalchemy.orm
移除
EXT_PASS
的别名EXT_CONTINUE
.sqlalchemy.engine
别名
DefaultDialect.preexecute_sequences
到.preexecute_pk_sequences
已删除。已删除已弃用的引擎描述符()函数。
sqlalchemy.ext.activemapper
模块被删除。
sqlalchemy.ext.assignmapper
模块被删除。
sqlalchemy.ext.associationproxy
传递代理服务器上的关键字参数
.append(item, \**kw)
已经被移除,现在只是.append(item)
sqlalchemy.ext.selectresults
,sqlalchemy.mods.selectresults
模块已移除。
sqlalchemy.ext.declarative
declared_synonym()
远离的。sqlalchemy.ext.sessioncontext
模块被删除。
sqlalchemy.log
这个
SADeprecationWarning
别名sqlalchemy.exc.SADeprecationWarning
已删除。sqlalchemy.exc
exc.AssertionError
已被删除,使用方法已被同名的python内置项替换。sqlalchemy.databases.mysql
被蔑视的
get_version_info
方言方法已被删除。
重命名或移动
sqlalchemy.exceptions
is nowsqlalchemy.exc
在0.6之前,模块仍可以旧名称导入。
FlushError
,ConcurrentModificationError
,UnmappedColumnError
->sqlacalchemy.orm.exc.执行这些异常移到ORM包中。导入“sqlachemy.orm”将在sqlachemy.exc中安装别名,以便在0.6之前保持兼容性。
sqlalchemy.logging
->sqlalchemy.log
此内部模块已重命名。当用py2app和扫描导入的类似工具包装SA时,不再需要特殊包装。
session.Query().iterate_instances()
>session.Query().instances()
.
已弃用
Session.save()
,Session.update()
,Session.save_or_update()
三个都被替换为
Session.add()
sqlalchemy.PassiveDefault
使用
Column(server_default=...)
转换为引擎盖下的sqlAlchemy.defaultClause()。session.Query().iterate_instances()
. 它已重命名为instances()
.