如何使用I18N的默认一对一属性创建一对多关系

时间:2013-08-18 22:20:10

标签: python sqlalchemy

假设我们有这些类:

class Item(Base):
    id = Column(Integer, primary_key=True)
    data = Column(String)
    i18ns = relationship("ItemI18n", backref="item")

class ItemI18n(Base):
    lang_short = Column(String, primary_key=True)
    item_id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    name = Column(String)

这里的想法是将这个项目的名称用多种语言,例如英语和德语。到目前为止这个工作正常,人们可以很容易地使用它。但是,大多数情况下,我对所有(即两个)名称都不感兴趣,只对用户区域设置感兴趣。

例如,如果用户是英语并且想要用他的语言命名,我会看到两个选项:

# Use it as a list
item = session.query(Item).first()
print item.data, [i18n.name for i18n in item.i18ns if i18n.lang_short == "en"][0]

# Get the name separately
item, name = session.query(Item, ItemI18N.name).join(ItemI18N).filter(ItemI18N.lang_short == "en").first()
print item.data, name

第一个过滤列表,第二个分别查询语言。第二种是更有效的方式,因为它只提取真正需要的数据。但是,有一个缺点:我现在必须携带两个变量:itemname。例如,如果我要扩展我的ItemI18N,请添加description属性,然后我会查询ItemI18N并随身携带。

但是业务逻辑是不同的:我希望Item具有namedescription属性,所以我会做这样的事情:

item = session.query(Item).first()
print item.data, item.name

这就是我要去的地方:将所有这些属性从Item18N直接拉到Item。当然,我必须在任何地方指定语言。但是,我找不到任何食谱,因为我甚至不知道要搜索什么。 SQLAlchemy能做这样的事吗?

我还为我描述的所有内容创建了complete example(当然除了我不知道如何实现的部分)。

编辑:我已经玩了很多,看看我能否提出更好的解决方案,到目前为止,我找到了一种方法。我最初试图用Query.get来实现它,但这不能超越我的简单例子,因为现实是不同的。要解释一下,我必须通过添加Language表来扩展我的初始模型,并将ItemI18N转换为主键为(lang_id, item_id)的多对多关系:

class ItemI18N(Base):
    lang_id = Column(Integer, ForeignKey('language.id'), primary_key=True)
    item_id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    name = Column(String)
    language = relationship("Language", lazy="joined")


class Language(Base):
    id = Column(Integer, primary_key=True)
    short = Column(String)

现在,为了获得正确的语言环境,我只需将lazy="joined"应用于完整路径,即可将所有加载项转换为连接加载项。这将不可避免地拉动所有语言,从而返回比我需要的更多的数据。我的方法完全独立于SQLAlchemy:

class Item(Base):
    ...
    i18ns = relationship("ItemI18N", backref="item", cascade="all, delete-orphan", lazy="joined")

    def name(self, locale):
        for i18n in self.i18ns:
            if i18n.language.short == locale:
                return i18n.name

但这不是一个漂亮的解决方案,因为从数据库中检索所有 I18N数据,然后将结果装回 one ,从而完全解决了这个问题无关紧要的是我把所有东西都放在首位(因为语言环境将始终保持不变)。我的new full example显示了如何只执行一个查询并提供透明访问 - 但是我想避免使用丑陋的开销。

该示例还包含了一些我已经完成的transformations游戏。这可以指向这个方向的解决方案,但我对此并不满意,因为它要求我每次都要通过with_transformation部分。如果在查询Item时自动应用,我会更喜欢它。但我没有发现任何事件或其他事件。

所以现在我有多个解决方案尝试,与上述业务逻辑相比,都缺乏直接访问的便利性。我希望有人能够弄清楚如何缩小这些差距,以产生美观和干净的东西。

0 个答案:

没有答案