使用SQLAlchemy ORM,如何监听对“noload”或“raise”设置为懒惰的关系的调用?

时间:2018-02-08 15:25:00

标签: python mysql caching sqlalchemy flask-sqlalchemy

我的样本表:

class TestEvent(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(20))

    def __str__(self):
        return f'{self.name} ({self.id})'


class TestMarket(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(20))

    def __str__(self):
        return f'{self.name} ({self.id})'


class TestEventMarket(Model):
    event_id = Column(Integer, ForeignKey(TestEvent.id))
    market_id = Column(Integer, ForeignKey(TestMarket.id))

    event = relationship(TestEvent, lazy='noload',
                         backref=backref('event_markets', lazy='dynamic'))
    market = relationship(TestMarket, lazy='noload',
                          backref=backref('event_markets', lazy='dynamic'))

    PrimaryKeyConstraint(event_id, market_id)

    def __str__(self):
        return f'{self.event_id}:{self.market_id}'

示例初始化代码:

event = TestEvent(name='test event1')
market = TestMarket(name='test market1')

em = TestEventMarket(event=event, market=market)

session.add(event)
session.add(market)
session.add(em)
session.commit()

示例查询代码:

event = TestEvent.query.first()
market = TestMarket.query.first()
em = TestEventMarket.query.first()

print(event, market, em)
print(list(map(str, event.event_markets)),
      list(map(str, market.event_markets)),
      em.event, em.market)

预计会有哪些产出:

test event1 (1) test market1 (1) 1:1
['1:1'] ['1:1'] None None

我想要这种行为的原因是因为,在我的应用程序中,在启动时,我得到所有“相关”TestEventTestMarket,并缓存它们。可以在生命周期的某个时刻创建新的TestEventTestMarket,但这只是添加到缓存中,所以一切正常。

这些是示例表,但我有许多其他表基本上作为Enum表运行,并在整个应用程序中使用。因为我有这些缓存,并且有一种“标准”方式来访问它们,所以我更愿意让数据库模型使用该方法。

我查看了SQLAlchemy文档,并认为它可以用他们的MappedCollection或者他们的HybridAttributes完成,但作为他们全新的概念,我是在我们周围缠绕我的头时会遇到一些麻烦,并弄清楚他们是否正好我正在寻找什么。或者我应该创建自定义InstrumentedAttribute并拥有自己的relationship?同样,它并没有“感觉”正确,我认为应该有更好的方法来实现我想要的。

我要覆盖或捕获的 main 是“获取”部分。我希望它能够根据我自己的应用程序逻辑返回一些内容,而不是任何内容或SQL查询。

1 个答案:

答案 0 :(得分:0)

一种“工作”的方法,但我认为丑陋如此,过度杀戮是在relationship内部声明的TestEventMarket,以及为调用设置的属性,

class TestEventMarket(Model):
    event_id = Column(Integer, ForeignKey(TestEvent.id))
    market_id = Column(Integer, ForeignKey(TestMarket.id))

    _event = relationship(TestEvent, lazy='noload',
                          backref=backref('event_markets', lazy='dynamic'))

    @property
    def event(self):
        if self._event:
            return self._event
        return get_event(self.event_id)

    @event.setter
    def event(self, event: TestEvent):
        if event.id:
            self.event_id = event.id
        self._event = event

    _market = relationship(TestMarket, lazy='noload',
                           backref=backref('event_markets', lazy='dynamic'))

    @property
    def market(self):
        if self._market:
            return self._market
        return get_market(self.market_id)

    @market.setter
    def market(self, market: TestMarket):
        if market.id:
            self.market_id = market.id
        self._market = market

    PrimaryKeyConstraint(event_id, market_id)

    def __str__(self):
        return f'{self.event_id}:{self.market_id}'