【问题标题】:SqlAlchemy attribute that tracks an assigned attribute跟踪分配属性的 SqlAlchemy 属性
【发布时间】:2016-03-15 13:52:03
【问题描述】:

目标:创建一个跟踪/跟踪另一个对象的 SQLAlchemy 属性更改的 SQLAlchemy 属性。

给定:

class ClazzA():
    attributeA = Column(JSONDict)

class ClazzB():
    attributeB = Column(?)

objectA = ClazzA()
objectA.attributeA = {'foo': 1}
objectB = ClazzB()
objectB.attributeB = objectA.attributeA
objectA.attributeA['foo'] = 2

JSONDictMutableDict 相关联,如下所述:http://docs.sqlalchemy.org/en/latest/orm/extensions/mutable.html#module-sqlalchemy.ext.mutable,即 JSONDict 类型允许突变跟踪。

所以我们在 objectA 上有了这个字典,它的变化正在被 SQLAlchemy 记录。我希望attributeB 跟踪attributeA,这样即使应用程序重新启动(即从DB 重新加载属性),attributeB 也将继续反映对attributeA 的字典所做的更改。

当然,这与 Python doesn't have an idea of pointers.我想知道 SQLAlchemy 是否有针对这个特定问题的解决方案。

【问题讨论】:

  • ClazzB 是另一个型号吗?您需要保留该模型吗?
  • @univerio 是的,ClazzB 也需要持久化。
  • 您的模型ClazzAClazzB 在数据库级别有什么明确的关系吗?换句话说,如果我们只是得到一个像“ClazzA.attributeA was changed for this instance”这样的事件,有没有办法说“这个 ClazzA 实例与那个 ClazzB 实例相关”?
  • @Ilja 这将是解决方案的一部分。在__init__objectB(或更高版本)期间,我们会将其指向objectA 上的预期/配对/匹配属性。 attributeB 只会指向 ClazzA 类型的对象上的 attributeA,只是直到运行时我们才知道哪个 objectA
  • @ChaimKut 听起来很像您正在寻找的是 ForeignKey + ClazzB 上的属性。

标签: python sqlalchemy


【解决方案1】:

TL;DR

你想要one-to-many 关系。

from sqlalchemy import ForeignKey, Integer, Column
from sqlalchemy.orm import relationship


class Widget(Base):

    __tablename__ = 'widget'

    widget_id = Column(Integer, primary_key=True)
    # name columns, type columns, ...
    json = Column(JSONDict)


class ClazzB(Base):

    __tablename__ = 'clazzb'

    clazzb_id = Column(Integer, primary_key=True)
    # Your "attributeB"
    widget_id = Column(Integer,
                       ForeignKey('widget.widget_id',
                                  onupdate='cascade',
                                  ondelete='cascade'),
                       nullable=False)

    widget = relationship('Widget')
    # possible association_proxy
    #widget_json = association_proxy('widget', 'json')

使用关系

在模型ClazzAClazzB 之间定义一个relationship。现在,由于我们没有完整的了解,以下定义只是示例。

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship


class ClazzA(Base):  # replace Base with the base class of your models

    __tablename__ = 'clazza'  # replace with the real tablename

    # T is the type of your primary key, the column name is just an example
    clazza_id = Column(T, primary_key=True)


class ClazzB(Base):

    # The column that will relate this model to ClazzA
    clazza_id = Column(T, ForeignKey('clazza.clazza_id',
                                     onupdate='cascade',
                                     ondelete='cascade'),
                       nullable=False)

    # A handy accessor for relationship between mapped classes,
    # not strictly required. Configurable to be either very lazy
    # (loaded if accessed by issuing a SELECT) or eager (JOINed
    # when loading objectB for example)
    objectA = relationship('ClazzA')

现在,不要在初始化时添加对ClazzAattributeA 的引用到ClazzB,而是添加对相关objectA 的引用到objectB

objectB = ClazzB(..., objectA=objectA)

这两者现在是相关的并且通过objectB访问相关objectAattributeA

objectB.objectA.attributeA

无需跟踪对attributeA 的更改,因为它是实例的attributeA

现在,如果您必须在 ClazzB 上有一个属性 attributeB(以避免重构现有代码或类似的东西),您可以添加一个属性

class ClazzB:

    @property
    def attributeB(self):
        return self.objectA.attributeA

这将返回相关objectAattributeA

objectB.attributeB
objectB.attributeB['something'] = 'else'

等等。

还有一种用于跨关系访问属性的 SQLAlchemy 方法:association proxy。它支持简单查询,但不能下标。

class ClazzB(Base):

    attributeB = association_proxy('objectA', 'attributeA')

如果您希望ClazzB.attributeB 在某个键下访问来自JSONDict 的值,例如,您可以使用类似这样的方法

class ClazzB(Base):

    key = Column(Unicode)

    @property
    def attributeB(self):
        return self.objectA.attributeA[self.key]

如果您需要这样的东西,您还可以使用hybrid properties 使attributeB 在类级别上作为SQL 表达式工作。不过,您必须自己编写类级别的表达式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多