用于跟踪指定属性的SqlAlchemy属性

Cha*_*Kut 0 python sqlalchemy

目标:创建一个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
Run Code Online (Sandbox Code Playgroud)

JSONDictMutableDict此处描述的相关联:http://docs.sqlalchemy.org/en/latest/orm/extensions/mutable.html#module-sqlalchemy.ext.mutable,即该JSONDict类型允许进行突变跟踪.

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

当然,这与Python 不知道指针的事实密切相关.我想知道SQLAlchemy是否有针对这个特定问题的解决方案.

Ilj*_*ilä 6

TL; DR

你想要一对多的关系.

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')
Run Code Online (Sandbox Code Playgroud)

使用关系

定义模型和模型之间的关系.既然我们没有全局,下面的定义就是例子.ClazzAClazzB

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')
Run Code Online (Sandbox Code Playgroud)

现在,而不是添加到参考attributeAClazzA,以ClazzB一个参考相关补充objectAobjectB上初始化.

objectB = ClazzB(..., objectA=objectA)
Run Code Online (Sandbox Code Playgroud)

这两个现在是相关的,并通过do 访问attributeA相关objectAobjectB

objectB.objectA.attributeA
Run Code Online (Sandbox Code Playgroud)

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

现在,如果你必须有一个属性attributeBClazzB(为了避免重构现有的代码或一些这样的),你可以添加一个属性

class ClazzB:

    @property
    def attributeB(self):
        return self.objectA.attributeA
Run Code Online (Sandbox Code Playgroud)

这将返回attributeA相关objectA

objectB.attributeB
objectB.attributeB['something'] = 'else'
Run Code Online (Sandbox Code Playgroud)

等等.

还有一种用于跨关系访问属性的SQLAlchemy方法:关联代理.它支持简单查询,但不是例如可订阅的.

class ClazzB(Base):

    attributeB = association_proxy('objectA', 'attributeA')
Run Code Online (Sandbox Code Playgroud)

如果您希望ClazzB.attributeBJSONDict某个键下面访问值,您可以使用例如这样的内容

class ClazzB(Base):

    key = Column(Unicode)

    @property
    def attributeB(self):
        return self.objectA.attributeA[self.key]
Run Code Online (Sandbox Code Playgroud)

如果需要这样的话,您还可以attributeB使用混合属性在类级别上作为SQL表达式工作.您必须自己编写类级表达式.