突变跟踪

优质
小牛编辑
149浏览
2023-12-01

支持跟踪对标量值的就地更改,这些更改将传播到所属父对象的ORM更改事件中。

建立标量列值的可变性

“可变”结构的一个典型例子是Python字典。遵循中介绍的示例 列和数据类型 ,我们从自定义类型开始,该类型在持久化之前将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类型可能可变的任何类型,包括 PickleTypeARRAY 等。

当使用 sqlalchemy.ext.mutable 扩展名,值本身跟踪引用它的所有父级。下面,我们演示了 MutableDict Dictionary对象,它应用 Mutable 混合到一个普通的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()

上面的dictionary类采用了对python内置子类化的方法 dict 产生一个dict子类,将所有突变事件通过 __setitem__ . 这种方法有一些变体,例如子类化 UserDict.UserDictcollections.MutableMapping ;对于这个例子来说,最重要的部分是 Mutable.changed() 每当发生对数据结构的就地更改时,都会调用方法。

我们还重新定义了 Mutable.coerce() 方法,该方法将用于转换不是 MutableDict ,例如 json 模块,放入适当的类型。定义这个方法是可选的;我们也可以创建 JSONEncodedDict 这样它总是返回 MutableDict 并确保所有调用代码都使用 MutableDict 明确地。什么时候? Mutable.coerce() 不重写,任何应用于父对象(不是可变类型的实例)的值都将引发 ValueError .

我们的新 MutableDict 类型提供类方法 Mutable.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))
)

上面, Mutable.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

这个 MutableDict 可以与以后的所有实例关联 JSONEncodedDict 一步到位,使用 Mutable.associate_with() . 这和 Mutable.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 在value对象上,它存储一个父映射对象的映射,这些对象的键控对象是与该值关联的属性名。 WeakKeyDictionary 对象是不可拾取的,因为它们包含weakerfs和函数回调。在我们的例子中,这是一件好事,因为如果这个字典是可挑选的,它可能会导致我们的值对象的pickle大小过大,而这些对象本身在父对象的上下文之外被pickle。开发者的职责只是提供 __getstate__ 排除 MutableBase._parents() 泡菜流收集:

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

使用字典示例,我们需要返回dict本身的内容(并在 __setstate__) ::

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

    def __getstate__(self):
        return dict(self)

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

如果可变值对象在附加到一个或多个也是pickle一部分的父对象时被pickle,则 Mutable Mixin将重新建立 Mutable._parents 取消拾取每个值对象上作为所属父对象本身的集合。

接收事件

这个 AttributeEvents.modified() 事件处理程序可用于在可变标量发出更改事件时接收事件。当 flag_modified() 从可变扩展名内调用函数:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import event

Base = declarative_base()

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

@event.listens_for(MyDataClass.data, "modified")
def modified_json(instance):
    print("json value modified:", instance.data)

确定复合材料的易变性

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

情况也是如此 Mutable ,用户定义的复合类子类 MutableComposite 作为一个混合,并通过 MutableComposite.changed() 方法。对于复合类,通常通过使用python描述符(即 @property 或者通过特殊的python方法 __setattr__() . 下面我们将展开 Point 在中引入的类 组合列类型 子类 MutableComposite 以及通过 __setattr__MutableComposite.changed() 方法:

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元类自动建立侦听器,以便使用 composite() 这说明了我们 Point 类型。下面,什么时候 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 , the MutableBase.coerce() 方法只对属性集操作调用,而不是对加载操作调用。重写 MutableBase.coerce() 方法本质上等同于使用 validates() 使用自定义复合类型的所有属性的验证例程:

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

配套酸洗

情况也是如此 Mutable , the MutableComposite 帮助程序类使用 weakref.WeakKeyDictionary 可通过 MutableBase._parents() 不可选择的属性。如果我们需要pickle Point 或者它所属的阶级 Vertex 我们至少需要定义 __getstate__ 不包括 _parents 字典。下面我们定义两个 __getstate__ 和A __setstate__ 把我们 Point 班级:

class Point(MutableComposite):
    # ...

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

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

和一样 Mutable , the MutableComposite 增加父对象关系状态的酸洗过程,以便 MutableBase._parents() 集合已还原为所有 Point 物体。

API引用

Object NameDescription

Mutable

定义将更改事件透明传播到父对象的mixin。

MutableBase

公共基类到 MutableMutableComposite .

MutableComposite

mixin,它定义了将SQLAlchemy“composite”对象上的更改事件透明传播到其所属父对象或父对象。

MutableDict

实现的字典类型 Mutable .

MutableList

实现的列表类型 Mutable .

MutableSet

实现的集合类型 Mutable .

class sqlalchemy.ext.mutable.MutableBase

公共基类到 MutableMutableComposite .

attribute sqlalchemy.ext.mutable.MutableBase._parents

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

此属性是所谓的“memoized”属性。它用一个新的 weakref.WeakKeyDictionary 第一次访问它时,在随后访问时返回同一对象。

在 1.4 版更改: 这个 InstanceState 现在用作弱字典中的键,而不是用作实例本身。

method sqlalchemy.ext.mutable.MutableBase.classmethod coerce(key, value)

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

可以被自定义子类重写,以将传入数据强制为特定类型。

默认情况下,加薪 ValueError .

根据父类的类型,在不同的方案中调用此方法 Mutable 或类型 MutableComposite . 对于前者,在属性集操作和ORM加载操作期间都会调用它。对于后者,只在属性集操作期间调用; composite() 在加载操作期间构造句柄强制。

参数
  • key -- 正在设置的ORM映射属性的字符串名称。

  • value -- 传入值。

返回

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

class sqlalchemy.ext.mutable.Mutable

定义将更改事件透明传播到父对象的mixin。

参见中的示例 建立标量列值的可变性 有关用法信息。

类签名

class sqlalchemy.ext.mutable.Mutable (sqlalchemy.ext.mutable.MutableBase)

method sqlalchemy.ext.mutable.Mutable.classmethod _get_listen_keys(attribute)

inherited from the sqlalchemy.ext.mutable.MutableBase._get_listen_keys method of MutableBase

给定一个描述符属性,返回 set() 指示此属性状态更改的属性键。

这通常只是 set([attribute.key]) ,但可以重写以提供其他键。例如 MutableComposite 使用与组成复合值的列关联的属性键扩充此集合。

如果截取 InstanceEvents.refresh()InstanceEvents.refresh_flush() 事件,传递已刷新的属性名列表;将列表与此集合进行比较,以确定是否需要执行操作。

1.0.5 新版功能.

method sqlalchemy.ext.mutable.Mutable.classmethod _listen_on_attribute(attribute, coerce, parent_cls)

inherited from the sqlalchemy.ext.mutable.MutableBase._listen_on_attribute method of MutableBase

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

attribute sqlalchemy.ext.mutable.Mutable._parents

inherited from the sqlalchemy.ext.mutable.MutableBase._parents attribute of MutableBase

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

此属性是所谓的“memoized”属性。它用一个新的 weakref.WeakKeyDictionary 第一次访问它时,在随后访问时返回同一对象。

在 1.4 版更改: 这个 InstanceState 现在用作弱字典中的键,而不是用作实例本身。

method sqlalchemy.ext.mutable.Mutable.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.associate_with() 特殊的分类方法 Mutable 子类以建立全局关联。

警告

通过这种方法建立的侦听器是 全球的 所有映射器,并且 not 垃圾收集。只使用 as_mutable() 对于应用程序的永久类型,而不是特殊类型,否则这将导致内存使用量的无限增长。

method sqlalchemy.ext.mutable.Mutable.classmethod associate_with(sqltype)

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

这是一个调用 associate_with_attribute 自动地。

警告

通过这种方法建立的侦听器是 全球的 所有映射器,并且 not 垃圾收集。只使用 associate_with() 对于应用程序的永久类型,而不是特殊类型,否则这将导致内存使用量的无限增长。

method sqlalchemy.ext.mutable.Mutable.classmethod associate_with_attribute(attribute)

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

method sqlalchemy.ext.mutable.Mutable.changed()

每当发生更改事件时,子类应该调用此方法。

method sqlalchemy.ext.mutable.Mutable.classmethod coerce(key, value)

inherited from the MutableBase.coerce() method of MutableBase

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

可以被自定义子类重写,以将传入数据强制为特定类型。

默认情况下,加薪 ValueError .

根据父类的类型,在不同的方案中调用此方法 Mutable 或类型 MutableComposite . 对于前者,在属性集操作和ORM加载操作期间都会调用它。对于后者,只在属性集操作期间调用; composite() 在加载操作期间构造句柄强制。

参数
  • key -- 正在设置的ORM映射属性的字符串名称。

  • value -- 传入值。

返回

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

class sqlalchemy.ext.mutable.MutableComposite

mixin,它定义了将SQLAlchemy“composite”对象上的更改事件透明传播到其所属父对象或父对象。

参见中的示例 确定复合材料的易变性 有关用法信息。

类签名

class sqlalchemy.ext.mutable.MutableComposite (sqlalchemy.ext.mutable.MutableBase)

method sqlalchemy.ext.mutable.MutableComposite.changed()

每当发生更改事件时,子类应该调用此方法。

class sqlalchemy.ext.mutable.MutableDict

实现的字典类型 Mutable .

这个 MutableDict 对象实现一个字典,当字典的内容发生更改时(包括添加或删除值时),该字典将向基础映射发出更改事件。

注意 MutableDictnot 将可变跟踪应用于 价值观本身 在字典里。因此,对于跟踪深度更改到 递归的 字典结构,如JSON结构。要支持此用例,请构建 MutableDict 它对字典中的值进行适当的强制,使它们也“可变”,并向其父结构发出事件。

参见

MutableList

MutableSet

类签名

class sqlalchemy.ext.mutable.MutableDict (sqlalchemy.ext.mutable.Mutable, builtins.dict)

method sqlalchemy.ext.mutable.MutableDict.clear() → None.  Remove all items from D.
method sqlalchemy.ext.mutable.MutableDict.classmethod coerce(key, value)

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

method sqlalchemy.ext.mutable.MutableDict.pop(k[, d]) → v, remove specified key and return the corresponding value.

如果找不到键,则返回d(如果给定),否则将引发keyError

method sqlalchemy.ext.mutable.MutableDict.popitem() → (k, v), remove and return some (key, value) pair as a

2元组;但如果d为空,则引发keyror。

method sqlalchemy.ext.mutable.MutableDict.setdefault(key, value)

如果关键字不在字典中,则插入值为默认值的关键字。

如果键在字典中,则返回键的值,否则为默认值。

method sqlalchemy.ext.mutable.MutableDict.update([E, ]**F) → None.  Update D from dict/iterable E and F.

如果e存在并且有.keys()方法,那么对e:d中的k执行:操作 [k] = e [k] 如果e存在,并且缺少.keys()方法,则为:对于k,e:d中的v [k] =v在任何一种情况下,后面跟着:对于f:d中的k [k] = f [k]

class sqlalchemy.ext.mutable.MutableList(iterable=(), /)

实现的列表类型 Mutable .

这个 MutableList 对象实现一个列表,该列表将在更改列表内容时(包括添加或删除值时)向基础映射发出更改事件。

注意 MutableListnot 将可变跟踪应用于 价值观本身 在列表中。因此,对于跟踪深度更改到 递归的 可变结构,如JSON结构。要支持此用例,请构建 MutableList 它对字典中的值进行适当的强制,使它们也“可变”,并向其父结构发出事件。

1.1 新版功能.

参见

MutableDict

MutableSet

类签名

class sqlalchemy.ext.mutable.MutableList (sqlalchemy.ext.mutable.Mutable, builtins.list)

method sqlalchemy.ext.mutable.MutableList.append(x)

将对象追加到列表末尾。

method sqlalchemy.ext.mutable.MutableList.clear()

从列表中删除所有项目。

method sqlalchemy.ext.mutable.MutableList.classmethod coerce(index, value)

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

method sqlalchemy.ext.mutable.MutableList.extend(x)

通过从iterable附加元素来扩展列表。

method sqlalchemy.ext.mutable.MutableList.insert(i, x)

在索引前插入对象。

method sqlalchemy.ext.mutable.MutableList.pop(*arg)

移除并返回索引处的项(默认最后一个)。

如果列表为空或索引超出范围,则引发IndexError。

method sqlalchemy.ext.mutable.MutableList.remove(i)

删除第一次出现的值。

如果值不存在,则引发ValueError。

method sqlalchemy.ext.mutable.MutableList.reverse()

反向 就位 .

method sqlalchemy.ext.mutable.MutableList.sort(**kw)

稳定排序 就位 .

class sqlalchemy.ext.mutable.MutableSet

实现的集合类型 Mutable .

这个 MutableSet 对象实现一个集合,该集合将在更改集合内容时(包括添加或删除值时)向基础映射发出更改事件。

注意 MutableSetnot 将可变跟踪应用于 价值观本身 在布景里。因此,对于跟踪深度更改到 递归的 可变结构。要支持此用例,请构建 MutableSet 它对字典中的值进行适当的强制,使它们也“可变”,并向其父结构发出事件。

1.1 新版功能.

参见

MutableDict

MutableList

类签名

class sqlalchemy.ext.mutable.MutableSet (sqlalchemy.ext.mutable.Mutable, builtins.set)

method sqlalchemy.ext.mutable.MutableSet.add(elem)

向集合中添加元素。

如果元素已经存在,则此操作无效。

method sqlalchemy.ext.mutable.MutableSet.clear()

删除此集合中的所有元素。

method sqlalchemy.ext.mutable.MutableSet.classmethod coerce(index, value)

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

method sqlalchemy.ext.mutable.MutableSet.difference_update(*arg)

从此集合中移除另一集合的所有元素。

method sqlalchemy.ext.mutable.MutableSet.discard(elem)

如果元素是成员,则将其从集合中移除。

如果元素不是成员,则不执行任何操作。

method sqlalchemy.ext.mutable.MutableSet.intersection_update(*arg)

用自身和另一个的交集更新集合。

method sqlalchemy.ext.mutable.MutableSet.pop(*arg)

移除并返回任意集合元素。如果集合为空,则引发keyError。

method sqlalchemy.ext.mutable.MutableSet.remove(elem)

从集合中移除元素;它必须是成员。

如果元素不是成员,则引发keyError。

method sqlalchemy.ext.mutable.MutableSet.symmetric_difference_update(*arg)

使用自身和另一个的对称差异更新集。

method sqlalchemy.ext.mutable.MutableSet.update(*arg)

使用自身和其他集合更新集合。