版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

SQLAlchemy 0.5有哪些新特性?

关于本文档

本文档介绍了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功能的介绍。特别是,QuerySession对象在API和行为方面有着明显的区别,从根本上改变了许多基本方式,特别是构建高度自定义的ORM查询并处理陈旧的会话状态,提交和回滚。

弃用来源

另一个信息来源被记录在一系列单元测试中,说明了一些常见的Query模式的最新用法;这个文件可以在[source:sqlalchemy / trunk / test / orm_test_deprecations.py]中查看。

需求变更

  • Python 2.4或更高版本是必需的。SQLAlchemy 0.4行是Python 2.3支持的最后一个版本。

对象关系映射

  • 查询中的列级表达式 - 详见教程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声明式]模块,它是用于将Tablemapper()和用户定义的类对象的表达式组合在一起,因为它简化了应用程序配置,确保“每个类一个映射器”模式,并允许全面的配置可用于不同的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.propnametable.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=FalseFlushes 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的参数,可以将其配置为selectdeferred该选项被删除;现在,映射器只会推迟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())

扩展ORM

在0.5中,我们正在采取更多方法来修改和扩展ORM。下面是一个总结:

  • MapperExtension。 T0> - 这是经典的扩展​​类,它仍然存在。Methods which should rarely be needed are create_instance() and populate_instance(). 要从数据库加载对象时控制对象的初始化,请使用文档中描述的reconstruct_instance()方法或更简单的@reconstructor装饰器。
  • SessionExtension。 T0> - 这是一个易于使用的会话事件扩展类。In particular, it provides before_flush(), after_flush() and after_flush_postexec() methods. 由于在before_flush()之内,您可以自由修改会话的刷新计划,因此建议在MapperExtension.before_XXX之上使用此用法,而MapperExtension
  • AttributeExtension。 T0> - 这个类现在是公共API的一部分,允许拦截属性上的userland事件,包括属性设置和删除操作以及集合附加和删除。它还允许修改或设置值。文档中描述的@validates装饰器提供了一种将任何映射属性标记为由特定类方法“验证”的快速方法。
  • 属性工具定制。 - 提供了一个API,用于完全替代SQLAlchemy的属性检测,或者仅仅在某些情况下对其进行扩充。该API是为了Trellis工具包的目的而生成的,但可作为公共API提供。/examples/custom_attributes目录中的分发中提供了一些示例。

模式/类型¶ T0>

  • 没有长度的字符串不会再生成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):
       # ...
  • ColumnTable以及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的DateTimeTimeDate类型现在只接受日期时间对象,而不接受字符串作为绑定参数输入。如果你想创建自己的“hybrid”类型,它接受字符串并将结果作为日期对象返回(从任何你想要的格式),创建一个TypeDecorator,它建立在String如果您只需要基于字符串的日期,只需使用String即可。

  • 此外,当与SQLite一起使用时,DateTimeTime类型现在表示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

默认情况下,连接池不再是threadlocal

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".

* args Accepted,* args不再被接受

使用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

除去¶ T0>

  • 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

    • 实例()
    • get_session() - 这个方法不是很明显,但是具有将惰性加载与特定会话相关联的效果,即使父对象完全分离,当扩展名如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.selectresultssqlalchemy.mods.selectresults

    模块已移除。

  • sqlalchemy.ext.declarative

    declared_synonym() removed.

  • sqlalchemy.ext.sessioncontext

    模块已移除。

  • sqlalchemy.log

    已将sqlalchemy.exc.SADeprecationWarningSADeprecationWarning别名删除。

  • sqlalchemy.exc

    exc.AssertionError已被删除,并且使用由内置相同名称的Python替换。

  • sqlalchemy.databases.mysql

    弃用的get_version_info方言方法已被删除。

重命名或移动

  • sqlalchemy.exceptions现在是sqlalchemy.exc

    该模块仍然可以以旧名称导入,直到0.6。

  • FlushErrorConcurrentModificationErrorUnmappedColumnError - > sqlalchemy.orm.exc

    这些异常移至orm包。导入'sqlalchemy.orm'将在sqlalchemy.exc中安装别名,直到0.6。

  • sqlalchemy.logging - > sqlalchemy.log

    此内部模块已重命名。使用py2app和类似工具扫描导入时,不再需要特殊的套件。

  • session.Query().iterate_instances() - > session.Query().instances()

弃用¶ T0>

  • Session.save(), Session.update(), Session.save_or_update()

    所有三个都被Session.add()取代

  • sqlalchemy.PassiveDefault

    使用Column(server_default=...)将其转换为sqlalchemy.DefaultClause()。

  • session.Query().iterate_instances()它已被重命名为instances()