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

SQLAlchemy 1.1文档

突变跟踪

提供对跟踪对标量值进行就地更改的支持,这些标量值将在拥有父对象时传播到ORM更改事件中。

New in version 0.7: sqlalchemy.ext.mutable replaces SQLAlchemy’s legacy approach to in-place mutations of scalar values; see Mutation event extension, supersedes “mutable=True”.

建立标量列值的可变性

一个“可变”结构的典型例子是Python字典。Column and Data Types中介绍的示例之后,我们从一个自定义类型开始,它将Python字典在持久化之前编组为JSON字符串:

from sqlalchemy.types import TypeDecorator, VARCHAR
import json

class JSONEncodedDict(TypeDecorator):
    "Represents an immutable structure as a json-encoded string."

    impl = VARCHAR

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value

json的用法仅用于示例。sqlalchemy.ext.mutable扩展名可用于目标Python类型可变的任何类型,包括PickleTypepostgresql.ARRAY等。

当使用sqlalchemy.ext.mutable扩展名时,该值本身会跟踪引用它的所有父项。下面,我们演示一下MutableDict字典对象的简单版本,它将Mutable mixin应用于普通的Python字典:

from sqlalchemy.ext.mutable import Mutable

class MutableDict(Mutable, dict):
    @classmethod
    def coerce(cls, key, value):
        "Convert plain dictionaries to MutableDict."

        if not isinstance(value, MutableDict):
            if isinstance(value, dict):
                return MutableDict(value)

            # this call will raise ValueError
            return Mutable.coerce(key, value)
        else:
            return value

    def __setitem__(self, key, value):
        "Detect dictionary set events and emit change events."

        dict.__setitem__(self, key, value)
        self.changed()

    def __delitem__(self, key):
        "Detect dictionary del events and emit change events."

        dict.__delitem__(self, key)
        self.changed()

上面的字典类采用子类化Python内置的dict的方法来生成一个dict子类,该子类通过__setitem__发送所有的突变事件。There are variants on this approach, such as subclassing UserDict.UserDict or collections.MutableMapping; the part that’s important to this example is that the Mutable.changed() method is called whenever an in-place change to the datastructure takes place.

我们还重新定义了将用于转换任何非MutableDict实例的值的Mutable.coerce()方法,例如由json模块,转换为适当的类型。定义此方法是可选的;我们也可以创建我们的JSONEncodedDict,以便它始终返回MutableDict的一个实例,并且确保所有调用代码明确使用MutableDictMutable.coerce()未被覆盖时,应用于不是可变类型实例的父对象的任何值将引发ValueError

我们新的MutableDict类型提供了一个类方法as_mutable(),我们可以在列元数据中使用它来与类型关联。此方法捕获给定类型的对象或类,并关联侦听器,该侦听器将检测此类型的所有未来映射,并将事件侦听工具应用于映射的属性。比如,用经典的表格元数据:

from sqlalchemy import Table, Column, Integer

my_data = Table('my_data', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MutableDict.as_mutable(JSONEncodedDict))
)

在上面,as_mutable()返回JSONEncodedDict的实例(如果类型对象不是已经存在的实例),它将拦截所有映射此类型的属性。下面我们根据my_data表建立一个简单映射:

from sqlalchemy import mapper

class MyDataClass(object):
    pass

# associates mutation listeners with MyDataClass.data
mapper(MyDataClass, my_data)

现在将通知MyDataClass.data成员更改其值。

使用声明时,使用方式没有区别:

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id = Column(Integer, primary_key=True)
    data = Column(MutableDict.as_mutable(JSONEncodedDict))

MyDataClass.data成员进行任何就地更改都会将该属性标记为父对象上的“脏”:

>>> from sqlalchemy.orm import Session

>>> sess = Session()
>>> m1 = MyDataClass(data={'value1':'foo'})
>>> sess.add(m1)
>>> sess.commit()

>>> m1.data['value1'] = 'bar'
>>> assert m1 in sess.dirty
True

使用associate_with()MutableDict可以与JSONEncodedDict的所有未来实例关联。这与as_mutable()类似,只是它将无条件地截取所有映射中所有出现的MutableDict,而不需要单独声明它:

MutableDict.associate_with(JSONEncodedDict)

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id = Column(Integer, primary_key=True)
    data = Column(JSONEncodedDict)

支持酸洗

sqlalchemy.ext.mutable扩展的关键依赖于weakref.WeakKeyDictionary在值对象上的位置,该对象存储了键入该属性的父映射对象的映射名称,它们与此值关联。WeakKeyDictionary objects are not picklable, due to the fact that they contain weakrefs and function callbacks. 在我们的例子中,这是一件好事,因为如果这个字典是可挑选的,它可能会导致我们的值对象的pickle大小过大,而这些对象是在父级上下文之外自行挑选的。这里的开发人员责任只是提供一个从pickle流中排除_parents()集合的__getstate__方法:

class MyMutableType(Mutable):
    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop('_parents', None)
        return d

用我们的字典例子,我们需要返回字典本身的内容(并且还在__ setstate__上恢复它们​​):

class MutableDict(Mutable, dict):
    # ....

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, state):
        self.update(state)

如果可变值对象被粘贴到一个或多个也是pickle一部分的父对象上,那么Mutable mixin将重新建立Mutable._parents

在复合材料上建立可变性

组合是一种特殊的ORM特性,它允许为单个标量属性分配一个对象值,该值表示来自底层映射表中一个或多个列的“合成”信息。通常的例子是几何“点”,并在Composite Column Types中介绍。

在版本0.7中更改: orm.composite()的内部已大大简化,默认情况下不再启用就地突变检测;相反,用户定义的值必须自行检测更改并将其传播给所有拥有的父项。sqlalchemy.ext.mutable扩展提供了helper类MutableComposite,它是Mutable类中的轻微变体。

Mutable一样,用户定义的组合类将MutableComposite作为一个mixin的子类,并通过MutableComposite.changed()方法。对于复合类,通常通过使用Python描述符(即@property)或者通过特殊的Python方法__setattr__()来进行检测。Below we expand upon the Point class introduced in Composite Column Types to subclass MutableComposite and to also route attribute set events via __setattr__ to the MutableComposite.changed() method:

from sqlalchemy.ext.mutable import MutableComposite

class Point(MutableComposite):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __setattr__(self, key, value):
        "Intercept set events"

        # set the attribute
        object.__setattr__(self, key, value)

        # alert all parents to the change
        self.changed()

    def __composite_values__(self):
        return self.x, self.y

    def __eq__(self, other):
        return isinstance(other, Point) and \
            other.x == self.x and \
            other.y == self.y

    def __ne__(self, other):
        return not self.__eq__(other)

The MutableComposite class uses a Python metaclass to automatically establish listeners for any usage of orm.composite() that specifies our Point type. 下面,当Point映​​射到Vertex类时,将建立侦听器,它将将Point对象的变化事件路由到Vertex.startVertex.end属性:

from sqlalchemy.orm import composite, mapper
from sqlalchemy import Table, Column

vertices = Table('vertices', metadata,
    Column('id', Integer, primary_key=True),
    Column('x1', Integer),
    Column('y1', Integer),
    Column('x2', Integer),
    Column('y2', Integer),
    )

class Vertex(object):
    pass

mapper(Vertex, vertices, properties={
    'start': composite(Point, vertices.c.x1, vertices.c.y1),
    'end': composite(Point, vertices.c.x2, vertices.c.y2)
})

Vertex.startVertex.end成员进行任何就地更改都会将该属性标记为父对象上的“脏”:

>>> from sqlalchemy.orm import Session

>>> sess = Session()
>>> v1 = Vertex(start=Point(3, 4), end=Point(12, 15))
>>> sess.add(v1)
>>> sess.commit()

>>> v1.end.x = 8
>>> assert v1 in sess.dirty
True

胁迫可变复合材料

复合类型也支持MutableBase.coerce()方法。MutableComposite的情况下,仅针对属性集操作调用MutableBase.coerce()方法,而不是调用操作。Overriding the MutableBase.coerce() method is essentially equivalent to using a validates() validation routine for all attributes which make use of the custom composite type:

class Point(MutableComposite):
    # other Point methods
    # ...

    def coerce(cls, key, value):
        if isinstance(value, tuple):
            value = Point(*value)
        elif not isinstance(value, Point):
            raise ValueError("tuple or Point expected")
        return value

New in version 0.7.10,0.8.0b2: Support for the MutableBase.coerce() method in conjunction with objects of type MutableComposite.

支持酸洗

As is the case with Mutable, the MutableComposite helper class uses a weakref.WeakKeyDictionary available via the MutableBase._parents() attribute which isn’t picklable. If we need to pickle instances of Point or its owning class Vertex, we at least need to define a __getstate__ that doesn’t include the _parents dictionary. 下面我们定义一个__getstate__和一个__setstate__,它们包装了我们的Point类的最小形式:

class Point(MutableComposite):
    # ...

    def __getstate__(self):
        return self.x, self.y

    def __setstate__(self, state):
        self.x, self.y = state

Mutable一样,MutableComposite增加了父对象关系状态的酸洗过程,以使MutableBase._parents()集合恢复为所有Point对象。

API参考

class sqlalchemy.ext.mutable。 MutableBase

通用基类为MutableMutableComposite

_parents T0> ¶ T1>

父对象上的字典 - >属性名称。

这个属性是一个所谓的“memoized”属性。它在第一次访问时使用新的weakref.WeakKeyDictionary进行初始化,并在后续访问时返回相同的对象。

classmethod coerce(key, value)

给定一个值,将其强制转换为目标类型。

可以被自定义子类覆盖以将输入数据强制转换为特定类型。

默认情况下,引发ValueError

根据父类是Mutable类型还是MutableComposite类型,在不同场景中调用此方法。在前者的情况下,它既被称为属性集操作,也被称为ORM加载操作。对于后者,它只在属性集操作期间被调用; composite()构造的机制在加载操作期间处理强制。

参数:
  • key – string name of the ORM-mapped attribute being set.
  • - 传入值。
返回:

该方法应该返回强制值,或者如果强制无法完成,则会引发ValueError

class sqlalchemy.ext.mutable。 Mutable

基础:sqlalchemy.ext.mutable.MutableBase

Mixin定义变化事件向父对象的透明传播。

有关使用信息,请参阅Establishing Mutability on Scalar Column Values中的示例。

__初始化__ T0> ¶ T1>
继承自 __init__ 属性 object

x .__ init __(...)初始化x;请参阅帮助(类型(x))进行签名

_get_listen_keys T0> ( T1> 属性 T2> ) T3> ¶ T4>
inherited from the _get_listen_keys() method of MutableBase

给定描述符属性,返回属性键的set(),它表示此属性状态的变化。

这通常只是set([attribute.key]),但可以被覆盖以提供额外的键。例如。一个MutableComposite使用与组成复合值的列相关联的属性键来扩充该集合。

在拦截InstanceEvents.refresh()InstanceEvents.refresh_flush()事件的情况下,会查看此集合,这些事件传递已刷新的属性名称列表;该列表与该组进行比较以确定是否需要采取行动。

版本1.0.5中的新功能

_listen_on_attribute(attribute, coerce, parent_cls)
inherited from the _listen_on_attribute() method of MutableBase

将此类型建立为给定映射描述符的变种侦听器。

_parents T0> ¶ T1>
inherited from the _parents attribute of MutableBase

父对象上的字典 - >属性名称。

这个属性是一个所谓的“memoized”属性。它在第一次访问时使用新的weakref.WeakKeyDictionary进行初始化,并在后续访问时返回相同的对象。

classmethod as_mutable(sqltype)

将SQL类型与此可变的Python类型关联。

这建立了侦听器,它将检测针对给定类型的ORM映射,并向这些映射添加突变事件跟踪器。

该类型作为一个实例无条件返回,因此as_mutable()可以内联使用:

Table('mytable', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MyMutableType.as_mutable(PickleType))
)

请注意,返回的类型始终是一个实例,即使给出了一个类,并且只有与该类型实例专门声明的列才会接收其他检测。

要将特定的可变类型与特定类型的所有匹配关联起来,请使用特定Mutable子类的Mutable.associate_with()类方法建立全局关联。

警告

由此方法建立的监听器对所有映射器都是全局,并且不是垃圾收集。对于应用程序永久性的类型,只能使用as_mutable(),否则会导致内存使用率无限增长。

classmethod associate_with(sqltype)

将此包装与给定类型的所有未来映射列相关联。

这是一种自动调用associate_with_attribute的便捷方法。

警告

由此方法建立的监听器对所有映射器都是全局,并且不是垃圾收集。对于应用程序永久性的类型,只能使用associate_with(),而不要使用ad-hoc类型,否则这会导致内存使用率无限增长。

classmethod associate_with_attribute(attribute)

将此类型建立为给定映射描述符的变种侦听器。

改变 T0> ( T1> ) T2> ¶ T3>

无论何时发生更改事件,子类都应调用此方法。

coerce(key, value)
inherited from the coerce() method of MutableBase

给定一个值,将其强制转换为目标类型。

可以被自定义子类覆盖以将输入数据强制转换为特定类型。

默认情况下,引发ValueError

根据父类是Mutable类型还是MutableComposite类型,在不同场景中调用此方法。在前者的情况下,它既被称为属性集操作,也被称为ORM加载操作。对于后者,它只在属性集操作期间被调用; composite()构造的机制在加载操作期间处理强制。

参数:
  • key – string name of the ORM-mapped attribute being set.
  • - 传入值。
返回:

该方法应该返回强制值,或者如果强制无法完成,则会引发ValueError

class sqlalchemy.ext.mutable。 MutableComposite

基础:sqlalchemy.ext.mutable.MutableBase

Mixin,定义SQLAlchemy“复合”对象上的变化事件的透明传播给其拥有的父对象或父对象。

有关使用信息,请参阅Establishing Mutability on Composites中的示例。

改变 T0> ( T1> ) T2> ¶ T3>

无论何时发生更改事件,子类都应调用此方法。

class sqlalchemy.ext.mutable。 MutableDict

基础:sqlalchemy.ext.mutable.Mutable__builtin__.dict

实现Mutable的字典类型。

MutableDict对象实现了一个字典,该字典在字典内容发生更改时(包括添加或删除值时)会将更改事件发送到基础映射。

Note that MutableDict does not apply mutable tracking to the values themselves inside the dictionary. 因此,对于追踪递归字典结构(如JSON结构)的深层变化的用例来说,这不是一个充分的解决方案。为了支持这个用例,构建一个MutableDict的子类,它为放置在字典中的值提供适当的转换,使它们也是“可变的”,并将事件发送到其父结构。

0.8版本中的新功能

也可以看看

MutableList

MutableSet

明确 T0> ( T1> ) T2> ¶ T3>
classmethod coerce(key, value)

将普通字典转换为此类的实例。

弹出 T0> ( T1> * ARG T2> ) T3> ¶ T4>
popitem T0> ( T1> ) T2> ¶ T3>
setdefault keyvalue
更新 * a** kw T5>
class sqlalchemy.ext.mutable。 MutableList

基础:sqlalchemy.ext.mutable.Mutable__builtin__.list

实现Mutable的列表类型。

MutableList对象实现了一个列表,当列表内容发生更改时(包括添加或删除值时),这些列表将发出更改事件到底层映射。

Note that MutableList does not apply mutable tracking to the values themselves inside the list. 因此,对于追踪递归可变结构(如JSON结构)的深层变化的用例来说,这不是一个充分的解决方案。为了支持这个用例,建立一个MutableList的子类,为放置在字典中的值提供适当的转换,以便它们也是“可变的”,并将事件发送到其父结构。

版本1.1中的新功能

也可以看看

MutableDict

MutableSet

追加 T0> ( T1> X T2> ) T3> ¶ T4>
明确 T0> ( T1> ) T2> ¶ T3>
classmethod coerce(index, value)

将普通列表转换为此类的实例。

延伸 T0> ( T1> X T2> ) T3> ¶ T4>
插入 ix
弹出 T0> ( T1> * ARG T2> ) T3> ¶ T4>
除去 T0> ( T1> I T2> ) T3> ¶ T4>
逆转 T0> ( T1> ) T2> ¶ T3>
排序 T0> ( T1> ) T2> ¶ T3>
class sqlalchemy.ext.mutable。 MutableSet

基础:sqlalchemy.ext.mutable.Mutable__builtin__.set

实现Mutable的集合类型。

MutableSet对象实现了一个集合,当集合的内容发生更改时(包括添加或删除值时),该集合将发生更改事件到底层映射。

Note that MutableSet does not apply mutable tracking to the values themselves inside the set. 因此,它不能用于追踪递归可变结构的深层变化的用例。为了支持这个用例,建立一个MutableSet的子类,为放置在字典中的值提供适当的转换,使它们也是“可变的”,并将事件发送到它们的父结构。

版本1.1中的新功能

也可以看看

MutableDict

MutableList

添加 T0> ( T1> ELEM T2> ) T3> ¶ T4>
明确 T0> ( T1> ) T2> ¶ T3>
classmethod coerce(index, value)

将普通集转换为此类的实例。

difference_update T0> ( T1> * ARG T2> ) T3> ¶ T4>
丢弃 T0> ( T1> ELEM T2> ) T3> ¶ T4>
intersection_update T0> ( T1> * ARG T2> ) T3> ¶ T4>
弹出 T0> ( T1> * ARG T2> ) T3> ¶ T4>
除去 T0> ( T1> ELEM T2> ) T3> ¶ T4>
symmetric_difference_update T0> ( T1> * ARG T2> ) T3> ¶ T4>
更新 T0> ( T1> * ARG T2> ) T3> ¶ T4>