版本: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.

我们还重新定义了Mutable.coerce()方法,该方法将用于转换任何非MutableDict实例的值,例如由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))
)

Above, as_mutable() returns an instance of JSONEncodedDict (if the type object was not an instance already), which will intercept any attributes which are mapped against this type. 下面我们根据my_data表建立一个简单的映射:

from sqlalchemy import mapper

class MyDataClass(object):
    pass

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

The MyDataClass.data member will now be notified of in place changes to its value.

使用声明时的用法没有区别:

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

Any in-place changes to the MyDataClass.data member will flag the attribute as “dirty” on the parent object:

>>> 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扩展的关键依赖于value对象的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扩展提供了MutableComposite这个助手类,它是Mutable类中的一个细微变体。

Mutable一样,用户定义的复合类将MutableComposite作为混合类型,并通过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)

MutableComposite类使用Python元类来自动为任何指定我们的Point类型的orm.composite()的用法建立侦听器。下面,当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”属性。It initializes itself with a new weakref.WeakKeyDictionary the first time it is accessed, returning the same object upon subsequent access.

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>
inherited from the __init__ attribute of 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”属性。It initializes itself with a new weakref.WeakKeyDictionary the first time it is accessed, returning the same object upon subsequent access.

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(),而不要使用ad-hoc类型,否则会导致内存使用率无限增长。

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(key, value)
更新 * a** kw T5>
class sqlalchemy.ext.mutable。 MutableList

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

实现Mutable的列表类型。

The MutableList object implements a list that will emit change events to the underlying mapping when the contents of the list are altered, including when values are added or removed.

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>
insert(i, x)
弹出 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>