2009-09-16 43 views
7

我有一個星型模式架構的數據庫,我想在SQLAlchemy中表示。現在我遇到了如何以最好的方式完成這個問題。現在我有很多自定義連接條件的屬性,因爲數據存儲在不同的表中。 這將是很好,如果有可能重新使用不同的事實tablesw維度,但我還沒有想出如何可以做得很好。SQLAlchemy中的星型模式

回答

19

星型模式中的典型事實表包含所有維表的外鍵引用,因此通常不需要自定義連接條件 - 它們是從外鍵引用自動確定的。

例如有兩個事實表星型模式會是什麼樣子:

Base = declarative_meta() 

class Store(Base): 
    __tablename__ = 'store' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(50), nullable=False) 

class Product(Base): 
    __tablename__ = 'product' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(50), nullable=False) 

class FactOne(Base): 
    __tablename__ = 'sales_fact_one' 

    store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True) 
    product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True) 
    units_sold = Column('units_sold', Integer, nullable=False) 

    store = relation(Store) 
    product = relation(Product) 

class FactTwo(Base): 
    __tablename__ = 'sales_fact_two' 

    store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True) 
    product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True) 
    units_sold = Column('units_sold', Integer, nullable=False) 

    store = relation(Store) 
    product = relation(Product) 

但是,假設你想減少在任何情況下樣板。我想創建本地發電機,其在事實表配置自己的維類:

其中
class Store(Base): 
    __tablename__ = 'store' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(50), nullable=False) 

    @classmethod 
    def add_dimension(cls, target): 
     target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True) 
     target.store = relation(cls) 

情況下使用會是這樣:

class FactOne(Base): 
    ... 

Store.add_dimension(FactOne) 

但是,有一個問題這一點。假設您要添加的維列是主鍵列,則映射器配置將失敗,因爲在映射設置之前,類需要設置其主鍵。因此,假設我們使用的聲明(你會看到下面有一個很好的效果),使這種方法的工作,我們不得不使用instrument_declarative()功能,而不是標準的元類:

meta = MetaData() 
registry = {} 
def register_cls(*cls): 
    for c in cls: 
     instrument_declarative(c, registry, meta) 

然後我們倒是做線沿線的東西:

class Store(object): 
    # ... 

class FactOne(object): 
    __tablename__ = 'sales_fact_one' 

Store.add_dimension(FactOne) 

register_cls(Store, FactOne) 

如果你確實有一個很好的理由,自定義連接條件,只要有一些模式是如何創造這些條件,您可以生成與add_dimension()

class Store(object): 
    ... 

    @classmethod 
    def add_dimension(cls, target): 
     target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True) 
     target.store = relation(cls, primaryjoin=target.store_id==cls.id) 

但是,如果你使用2.6,最後一件很酷的事情就是把add_dimension變成一個類裝飾器。這裏有一個清理一切的例子:

from sqlalchemy import * 
from sqlalchemy.ext.declarative import instrument_declarative 
from sqlalchemy.orm import * 

class BaseMeta(type): 
    classes = set() 
    def __init__(cls, classname, bases, dict_): 
     klass = type.__init__(cls, classname, bases, dict_) 
     if 'metadata' not in dict_: 
      BaseMeta.classes.add(cls) 
     return klass 

class Base(object): 
    __metaclass__ = BaseMeta 
    metadata = MetaData() 
    def __init__(self, **kw): 
     for k in kw: 
      setattr(self, k, kw[k]) 

    @classmethod 
    def configure(cls, *klasses): 
     registry = {} 
     for c in BaseMeta.classes: 
      instrument_declarative(c, registry, cls.metadata) 

class Store(Base): 
    __tablename__ = 'store' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(50), nullable=False) 

    @classmethod 
    def dimension(cls, target): 
     target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True) 
     target.store = relation(cls) 
     return target 

class Product(Base): 
    __tablename__ = 'product' 

    id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(50), nullable=False) 

    @classmethod 
    def dimension(cls, target): 
     target.product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True) 
     target.product = relation(cls) 
     return target 

@Store.dimension 
@Product.dimension 
class FactOne(Base): 
    __tablename__ = 'sales_fact_one' 

    units_sold = Column('units_sold', Integer, nullable=False) 

@Store.dimension 
@Product.dimension 
class FactTwo(Base): 
    __tablename__ = 'sales_fact_two' 

    units_sold = Column('units_sold', Integer, nullable=False) 

Base.configure() 

if __name__ == '__main__': 
    engine = create_engine('sqlite://', echo=True) 
    Base.metadata.create_all(engine) 

    sess = sessionmaker(engine)() 

    sess.add(FactOne(store=Store(name='s1'), product=Product(name='p1'), units_sold=27)) 
    sess.commit() 
+0

非常漂亮的設計 - 美麗! – 2012-06-05 12:36:06

+0

受此啓發,我終於想出瞭如何將配置傳遞給declared_attr,以便可以使庫知道主機應用程序模型:https://gist.github.com/miohtama/844cc78bcf1d317e31ca – 2015-06-07 04:07:59