关于本文档
本文档介绍了2008年10月12日发布的SQLAlchemy 0.4版和2010年1月16日发布的SQLAlchemy 0.5版之间的变化。
文件日期:2009年8月4日
本指南记录了影响用户将他们的应用程序从0.4系列SQLAlchemy迁移到0.5的API更改。它也适用于基本SQLAlchemy中的工作,它只涵盖了0.4,并且似乎甚至还有一些旧的0.3ism。请注意,SQLAlchemy 0.5删除了许多在0.4系列范围内被弃用的行为,并且也弃用了更多特定于0.4的行为。
文档的某些部分已经完全重写,可以作为新ORM功能的介绍。特别是,Query
和Session
对象在API和行为方面有着明显的区别,从根本上改变了许多基本方式,特别是构建高度自定义的ORM查询并处理陈旧的会话状态,提交和回滚。
另一个信息来源被记录在一系列单元测试中,说明了一些常见的Query
模式的最新用法;这个文件可以在[source:sqlalchemy / trunk / test / orm_test_deprecations.py]中查看。
查询中的列级表达式 - 详见教程,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
has a statement
accessor, as well as a subquery()
method which allow Query
to be used to create more complex combinations:
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)
)
Explicit ORM aliases are recommended for aliased joins - The aliased()
function produces an “alias” of a class, which allows fine-grained control of aliases in conjunction with ORM queries. 尽管表级别别名(即table.alias()
)仍然可用,但ORM级别别名保留了ORM映射对象的语义,这对于继承映射,选项和其他场景很重要。例如。:
Friend = aliased(Person)
session.query(Person, Friend).join((Friend, Person.friends)).all()
query.join()大大增强。 - 您现在可以通过多种方式指定联接的目标和ON子句。可以提供单独的目标类,其中SQLA将尝试通过与table.join(someothertable)
相同的方式通过外键形成连接。可以提供一个目标和一个明确的ON条件,其中ON条件可以是relation()
名称,实际类描述符或SQL表达式。或者只是relation()
名称或类描述符的旧方法也适用。请参阅包含几个示例的ORM教程。
声明式推荐用于不需要(且不喜欢)表和映射器之间的抽象的应用程序。 - [/docs/05/reference/ext/declarative.html声明式]模块,它是用于将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
类绑定描述符支持完整的Column运算符以及文档化的面向关系的运算符,如has()
,any()
,contains()
等
严重移除.c.
的原因 is that in 0.5, class-bound descriptors carry potentially different meaning, as well as information regarding class mappings, versus plain Column
objects - and there are use cases where you’d specifically want to use one or the other. 通常,使用类绑定描述符会调用一组映射/多态感知的转换,而使用表绑定的列则不会。在0.4中,这些翻译被全面应用于所有表达式,但是0.5与列和映射描述符完全不同,只是将翻译应用于后者。因此,在很多情况下,特别是在处理连接表继承配置以及使用query(<columns>)
,Class.propname
和table.c.colname
不可互换。
For example, session.query(users.c.id, users.c.name)
is different versus session.query(User.id, User.name)
; in the latter case, the Query
is aware of the mapper in use and further mapper-specific operations like query.join(<propname>)
, query.with_parent()
etc. 可能会使用,但在前者情况下不能。此外,在多态继承场景中,类绑定描述符引用可用的多态可选列中的列,而不一定是直接对应于描述符的表列。For example, a set of classes related by joined-table inheritance to the person
table along the person_id
column of each table will all have their Class.person_id
attribute mapped to the person_id
column in person
, and not their subclass table. 版本0.4将自动将此行为映射到表格绑定的Column
对象。In 0.5, this automatic conversion has been removed, so that you in fact can use table-bound columns as a means to override the translations which occur with polymorphic querying; this allows Query
to be able to create optimized selects among joined-table or concrete-table inheritance setups, as well as portable subqueries, etc.
Session Now Synchronizes Automatically with Transactions.会话现在默认会自动与事务同步,包括autoflush和autoexpire。除非禁止使用autocommit
选项,否则交易始终存在。当所有三个标志都设置为默认值时,会话在回滚之后会优雅地恢复,并且很难将陈旧的数据导入会话。有关详细信息,请参阅新的Session文档。
隐式订单被移除。这将影响依赖于SA的“隐式排序”行为的ORM用户,这些用户声明所有没有order_by()
的Query对象将ORDER BY的“id”或“oid”列主映射表和所有懒惰/急切加载的集合都会应用类似的排序。在0.5中,必须在mapper()
和relation()
对象(如果需要)上显式配置自动排序,否则在使用Query
时自动排序。
To convert an 0.4 mapping to 0.5, such that its ordering behavior will be extremely similar to 0.4 or previous, use the order_by
setting on mapper()
and 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))
使用声明?To help with the new order_by
requirement, order_by
and friends can now be set using strings which are evaluated in Python later on (this works only with declarative, not plain mappers):
class MyClass(MyDeclarativeBase):
...
'addresses':relation("Address", order_by="Address.id")
在relation()s
上设置order_by
通常是个好主意,因为该顺序不会受到影响。除此之外,最佳做法是使用Query.order_by()
来控制正在加载的主要实体的排序。
会话现在是autoflush = True / autoexpire = True / autocommit = False。 - 要设置它,只需调用不带参数的sessionmaker()
即可。名称transactional=True
现在是autocommit=False
。Flushes occur upon each query issued (disable with autoflush=False
), within each commit()
(as always), and before each begin_nested()
(so rolling back to the SAVEPOINT is meaningful). 所有对象都在每个commit()
之后和每个rollback()
之后过期。回滚后,挂起的对象被清除,被删除的对象移回到持久性。这些默认值一起工作得非常好,并且实际上不再需要像clear()
这样的旧技术(它也重新命名为expunge_all()
)。
附::会话现在可以在rollback()
之后重用。标量和集合属性的变化,添加和删除都回滚。
session.add() replaces session.save(), session.update(), session.save_or_update(). - the session.add(someitem)
and session.add_all([list of items])
methods replace save()
, update()
, and save_or_update()
. 这些方法将在整个0.5中保持不推荐。
backref配置没有冗长。 - The backref()
function now uses the primaryjoin
and secondaryjoin
arguments of the forwards-facing relation()
when they are not explicitly stated. 不再需要分别在两个方向上指定primaryjoin
/ secondaryjoin
。
简化的多态选项。 - ORM的“多态负载”行为已被简化。在0.4中,mapper()有一个名为polymorphic_fetch
的参数,可以将其配置为select
或deferred
。该选项被删除;现在,映射器只会推迟SELECT语句中不存在的任何列。The actual SELECT statement used is controlled by the with_polymorphic
mapper argument (which is also in 0.4 and replaces select_table
), as well as the with_polymorphic()
method on Query
(also in 0.4).
对继承类的延迟加载的一个改进是映射器现在在所有情况下都产生了SELECT语句的“优化”版本;也就是说,如果B类从A继承,并且只有B类中存在的几个属性已过期,则刷新操作将只在SELECT语句中包含B的表,并且不会加入A.
Session
上的execute()
方法将普通字符串转换为text()
结构,以便绑定参数可以全部指定为“:bindname”而不需要明确地调用text()
。如果需要“原始”SQL,请使用session.connection()。execute(“raw text”)
。
session.Query().iterate_instances()
已被重命名为instances()
。返回列表而不是迭代器的旧instances()
方法不再存在。如果你依赖这种行为,你应该使用list(your_query.instances())
。
在0.5中,我们正在采取更多方法来修改和扩展ORM。下面是一个总结:
create_instance()
and populate_instance()
. 要从数据库加载对象时控制对象的初始化,请使用文档中描述的reconstruct_instance()
方法或更简单的@reconstructor
装饰器。before_flush()
, after_flush()
and after_flush_postexec()
methods. 由于在before_flush()
之内,您可以自由修改会话的刷新计划,因此建议在MapperExtension.before_XXX
之上使用此用法,而MapperExtension
@validates
装饰器提供了一种将任何映射属性标记为由特定类方法“验证”的快速方法。/examples/custom_attributes
目录中的分发中提供了一些示例。没有长度的字符串不会再生成TEXT,它会生成VARCHAR - 当指定长度时,String
类型不再奇迹般地转换为Text
。这仅在发出CREATE TABLE时有效,因为它将发出没有长度参数的VARCHAR
,这在许多(但不是全部)数据库上都无效。要创建TEXT(或CLOB,即无界字符串)列,请使用Text
类型。
具有mutable = True的PickleType()需要__eq __()方法 - PickleType
类型需要在mutable = True时比较值。比较pickle.dumps()
的方法是低效且不可靠的。如果传入的对象没有实现__eq__()
并且也不是None
,则使用dumps()
比较,但会引发警告。对于实现包含所有字典,列表等的__eq__()
的类型,比较将使用==
,现在默认情况下可靠。
convert_bind_param() and convert_result_value() methods of TypeEngine/TypeDecorator are removed. - 不幸的是,O'Reilly的书中记录了这些方法,即使它们在0.3后被弃用。对于类型为TypeEngine
的用户定义类型,应该使用bind_processor()
和result_processor()
方法进行绑定/结果处理。任何用户定义的类型,无论是扩展TypeEngine
还是TypeDecorator
,都可以使用以下适配器轻松适应新样式:
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
要使用上面的mixin:
class MyType(AdaptOldConvertMethods, TypeEngine):
# ...
Column
和Table
以及Table
中的quote_schema
标志上的quote
默认值是None
,这意味着让常规引用规则生效。当True
时,强制引用引用。当False
时,引用被强制关闭。
列DEFAULT
值DDL现在可以通过Column(..., server_default ='val') t2 >,弃用
列(..., PassiveDefault('val'))
。default=
现在专用于Python启动的默认值,并且可以与server_default共存。新的server_default=FetchedValue()
取代了用于标记列的PassiveDefault('')
成语受外部触发影响并且没有DDL副作用。
SQLite的DateTime
,Time
和Date
类型现在只接受日期时间对象,而不接受字符串作为绑定参数输入。如果你想创建自己的“hybrid”类型,它接受字符串并将结果作为日期对象返回(从任何你想要的格式),创建一个TypeDecorator
,它建立在String
如果您只需要基于字符串的日期,只需使用String
即可。
此外,当与SQLite一起使用时,DateTime
和Time
类型现在表示Python datetime.datetime
对象的“microseconds”字段与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之间使用,则必须升级日期时间列以存储新格式(请注意:请测试一下,我非常肯定它的正确性):
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。To re-enable 0.4’s behavior, specify pool_threadlocal=True
to create_engine()
, or alternatively use the “threadlocal” strategy via strategy="threadlocal"
.
使用method(\*args)
与method([args])
的策略是,如果方法接受表示固定结构的可变长度项集,它需要\*args
。如果该方法接受数据驱动的可变长度项目集合,则需要[args]
。
The various Query.options() functions eagerload()
, eagerload_all()
, lazyload()
, contains_eager()
, defer()
, undefer()
all accept variable-length \*keys
as their argument now, which allows a path to be formulated using descriptors, ie. :
query.options(eagerload_all(User.orders, Order.items, Item.keywords))
为了向后兼容,仍然接受单个数组参数。
Similarly, the Query.join()
and Query.outerjoin()
methods accept a variable length *args, with a single array accepted for backwards compatibility:
query.join('orders', 'items')
query.join(User.orders, Order.items)
列和类似的_()方法中的in_()
它不再接受\*args
。
entity_name - 此功能始终存在问题并且很少使用。0.5的更深入充实的用例揭示了导致其被删除的entity_name
的进一步问题。如果单个类需要不同的映射,请将该类拆分为单独的子类并分别映射它们。一个例子是[wiki:UsageRecipes / EntityName]。有关基本原理的更多信息,请参见http://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)
and Session.load(cls, id)
have been removed. Session.get()
is redundant vs. 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()
以下弃用函数已被删除:
列表,标量,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
Additionally, the id
keyword argument to join()
, outerjoin()
, add_entity()
and add_column()
has been removed. 要将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
scoped_session()
或旧使用SessionContextExt
。一些依赖此行为的应用程序可能不再按预期工作;但是更好的编程习惯是在需要从属性访问数据库时始终确保对象存在于会话中。mapper(MyClass, mytable)
映射类no更长,具有“c”类属性;例如MyClass.c
sqlalchemy.orm.collections
prepare_instrumentation的_prepare_instrumentation别名已被删除。
sqlalchemy.orm
删除了EXT_PASS
EXT_CONTINUE
的别名。
sqlalchemy.engine
从DefaultDialect.preexecute_sequences
到.preexecute_pk_sequences
的别名已被删除。
已弃用的engine_descriptors()函数已被删除。
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()
removed.
sqlalchemy.ext.sessioncontext
模块已移除。
sqlalchemy.log
已将sqlalchemy.exc.SADeprecationWarning
的SADeprecationWarning
别名删除。
sqlalchemy.exc
exc.AssertionError
已被删除,并且使用由内置相同名称的Python替换。
sqlalchemy.databases.mysql
弃用的get_version_info
方言方法已被删除。
sqlalchemy.exceptions
现在是sqlalchemy.exc
该模块仍然可以以旧名称导入,直到0.6。
FlushError
,ConcurrentModificationError
,UnmappedColumnError
- > sqlalchemy.orm.exc
这些异常移至orm包。导入'sqlalchemy.orm'将在sqlalchemy.exc中安装别名,直到0.6。
sqlalchemy.logging
- > sqlalchemy.log
此内部模块已重命名。使用py2app和类似工具扫描导入时,不再需要特殊的套件。
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()
。