版本: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更改。It’s also recommended for those working from Essential SQLAlchemy, which only covers 0.4 and seems to even have some old 0.3isms in it. 请注意,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() greatly enhanced. - 现在可以通过多种方式为连接指定目标和ON子句。可以提供单独的目标类,SQLA将尝试通过外键与table.join(someothertable)相同的方式与它建立连接。可以提供一个目标和一个明确的ON条件,其中ON条件可以是一个relation()名称,一个实际的类描述符或一个SQL表达式。或者只是一个relation()名称或类描述符的旧方法。看看有几个例子的ORM教程。

  • Declarative is recommended for applications which don’t require (and don’t prefer) abstraction between tables and mappers - The [/docs/05/reference/ext/declarative.html Declarative] module, which is used to combine the expression of Table, mapper(), and user defined class objects together, is highly recommended as it simplifies application configuration, ensures the “one mapper per class” pattern, and allows the full range of configuration available to distinct mapper() calls. 单独的mapper()Table用法现在被称为“古典SQLAlchemy用法”,当然可以自由地与声明混合。

  • The .c. attribute has been removed from classes (i.e. MyClass.c.somecolumn). As is the case in 0.4, class- level properties are usable as query elements, i.e. Class.c.propname is now superseded by Class.propname, and the c attribute continues to remain on Table objects where they indicate the namespace of Column objects present on the table.

    为了得到一个映射类的表(如果你没有保留它已经):

    table = class_mapper(someclass).mapped_table

    遍历列:

    for col in table.c:
        print(col)

    使用特定的列:

    table.c.somecolumn

    这个类绑定的描述符支持完整的Column运算符以及像has()any()contains()

    难以清除.c.的原因在0.5中,类绑定描述符带有潜在的不同含义,以及有关类映射的信息,而不是普通的Column对象 - 并且有一些用例特别需要使用一个或者其他。通常,使用类绑定描述符调用一组映射/多态感知的翻译,而使用表绑定列则不会。在0.4中,这些翻译被全面应用于所有的表达,但是0.5与列和映射描述符完全区分,只是将翻译应用到后者。So in many cases, particularly when dealing with joined table inheritance configurations as well as when using query(<columns>), Class.propname and table.c.colname are not interchangeable.

    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对象。在0.5中,这个自动转换已经被删除了,所以实际上可以使用表绑定列作为覆盖多态查询发生的翻译的手段;这允许Query能够在连接表或具体表继承设置以及可移植子查询等中创建优化的选择。

  • Session Now Synchronizes Automatically with Transactions.会话现在默认自动同步事务,包括自动刷新和自动展开。除非使用autocommit选项禁用,否则交易始终存在。当所有三个标志都设置为默认值时,会话在回滚之后会优雅地恢复,并且很难将陈旧的数据导入到会话中。有关详细信息,请参阅新的会话文档

  • 隐式订单被移除这将影响依赖于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))

    使用声明?为了满足新的order_by要求,order_by和朋友现在可以使用稍后在Python中评估的字符串来设置(声明式的,不是普通的映射器):

    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 configuration made less verbose. - 现在,backref()函数使用前向relation()primaryjoinsecondaryjoin他们没有明确表示。不再需要分别在两个方向上指定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() has been renamed to just 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类型。

  • PickleType() with mutable=True requires an __eq__() method - The PickleType type needs to compare values when 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后被弃用。For a user-defined type which subclasses TypeEngine, the bind_processor() and result_processor() methods should be used for bind/result processing. 任何用户定义的类型,无论是扩展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上的quote标志以及Table上的quote_schema现在控制正反两方面的引用。默认是None,这意味着让正常的引用规则生效。True时,强制引用。False时,引用被强制关闭。

  • DEFAULT值DDL现在可以通过Column(..., server_default ='val') t2 >,弃用列(..., PassiveDefault('val'))default=现在专门用于Python启动的默认值,并且可以与server_default共存。一个新的server_default=FetchedValue()将标记列的PassiveDefault('')成语替换为受外部触发影响并且没有DDL副作用。

  • SQLite’s DateTime, Time and Date types now only accept datetime objects, not strings as bind parameter input. 如果你想创建自己的“混合”类型的接受字符串,并返回结果作为日期对象(从任何格式,你想),创建一个TypeDecorator建立在String如果你只想要基于字符串的日期,只需使用String即可。

  • Additionally, the DateTime and Time types, when used with SQLite, now represent the “microseconds” field of the Python datetime.datetime object in the same manner as str(datetime) - as fractional seconds, not a count of microseconds. 那是:

    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’s more deeply fleshed out use cases revealed further issues with entity_name which led to its removal. 如果单个类需要不同的映射,请将该类拆分为单独的子类并分别映射它们。一个例子是在[wiki:UsageRecipes / EntityName]。有关基本原理的更多信息,请参阅http://groups.google.com/ 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() is also removed (as is 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

    已删除SADeprecationWarning别名sqlalchemy.exc.SADeprecationWarning

  • sqlalchemy.exc

    exc.AssertionError has been removed and usage replaced by the Python built-in of the same name.

  • 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()