2011-10-06 49 views
1

對於Python,SQLAlchemy和Postgresql都很新近(但不是新生兒),並且很難理解繼承。Python,SQLAlchemy和Postgresql:理解繼承

當我接管另一個程序員的代碼時,我需要了解什麼是必要的,以及在哪裏,爲繼承概念工作。

我的問題是:

  1. 是否有可能只在SQLAlchemy的依靠繼承?換句話說,SQLAlchemy可以在沒有指定INHERITS =的情況下創建的Postgresql數據庫表上應用繼承嗎?

  2. declarative_base技術(SQLAlchemy)是否需要正確使用繼承。如果是這樣,我們將不得不重寫所有內容,所以請不要阻止我。

  3. 假設我們可以使用Table實例,空實體類和mapper(),你能否給我一個(非常簡單的)如何正確處理這個過程的例子(或者鏈接到一個容易理解的教程 - 我做過沒有找到任何足夠簡單的)。

我們正在研究的真實世界是房地產對象。所以我們基本上 - 一個表immobject(ID,createtime) - 一個表objectattribute(ID,immoobject_id,oatype) - 幾個屬性表:oa_attributename(oa_id,的AttributeValue)

感謝您的幫助提前。

Vincent

回答

4

歡迎來到堆棧溢出:在未來,如果您有多個問題,你應該爲每個提供一個單獨的帖子。如果它可能有助於提供上下文,請隨意將它們鏈接在一起。

postgres中的表繼承是一個非常不同的事情,它解決了python中類繼承的一系列不同問題,而sqlalchemy並未嘗試將它們結合起來。

當你在postgres中使用表繼承時,你在模式級別上做了一些小技巧,這樣就可以執行更復雜的約束,而不是以其他方式表達;一旦你設計了你的模式,應用程序通常不會意識到繼承;如果他們插入一行;它神奇地出現在父表中(很像一個視圖)。例如,這對於使某些批量操作更加高效(您可以放棄1月份的表格)非常有用。

這與從OOP中看到的繼承(在python或其他方面,具有關係持久性或其他方面)中的繼承有着根本不同的想法。在這種情況下,應用程序意識到兩種類型是相關的,並且子類型是超類型的允許替代。 「持有是地址,聯繫人有地址,因此聯繫人可以持有。」

您需要哪些(主要是正交)工具取決於應用程序。你可能不需要,你可能都需要。


  1. SQLAlchemy的的機制與對象繼承工作是靈活和強大,你應該有利於家庭構建的解決方案的使用它,如果它與您的特定需求(兼容這應該是真實的,幾乎所有的應用程序)。

  2. 聲明性擴展是一種方便;它允許你描述映射表,python類以及兩者之間的映射,而不是三個。它使你的代碼更「幹」;然而,它只是一個方便的「經典sqlalchemy」的頂層,並沒有任何必要的措施。

  3. 如果您發現需要從sqlalchemy中可見的表繼承;你的映射類與沒有使用這些特性沒有什麼不同;具有繼承的表仍然是正常的關係(如表或視圖),並且可以在不知道Python代碼中的繼承的情況下進行映射。

+0

+1感謝您解釋表繼承。 – wberry

0

對於你的#3,你不一定要聲明空實體類來使用mapper。如果您的應用程序不需要花哨的屬性,那麼您可以使用自省和元類對現有表進行建模,而無需對其進行定義。下面是我所做的:

mymetadata = sqlalchemy.MetaData() 
myengine = sqlalchemy.create_engine(...) 

def named_table(tablename): 
    u"return a sqlalchemy.Table object given a SQL table name" 
    return sqlalchemy.Table(tablename, mymetadata, autoload=True, autoload_with=myengine) 

def new_bound_class(engine, table): 
    u"returns a new ORM class (processed by sqlalchemy.orm.mapper) given a sqlalchemy.Table object" 
    fieldnames = table.c.__dict__['_data'] 
    def format_attributes(obj, transform): 
    attributes = [u'%s=%s' % (x, transform(x)) for x in fieldnames] 
    return u', '.join(attributes) 
    class DynamicORMClass(object): 
    def __init__(self, **kw): 
     u"Keyword arguments may be used to initialize fields/columns" 
     for key in kw: 
     if key in fieldnames: setattr(self, key, kw[key]) 
     else: raise KeyError, '%s is not a valid field/column' % (key,) 
    def __repr__(self): 
     return u'%s(%s)' % (self.__class__.__name__, format_attributes(self, repr)) 
    def __str__(self): 
     return u'%s(%s)' % (str(self.__class__), format_attributes(self, str)) 
    DynamicORMClass.__doc__ = u"This is a dynamic class created using SQLAlchemy based on table %s" % (table,) 
    return sqlalchemy.orm.mapper(DynamicORMClass, table) 

def named_orm_class(table): 
    u"returns a new ORM class (processed by sqlalchemy.orm.mapper) given a table name or object" 
    if not isinstance(table, Table): 
    table = named_table(table) 
    return new_bound_class(table) 

使用示例:

>>> myclass = named_orm_class('mytable') 
>>> session = Session() 
>>> obj = myclass(name='Fred', age=25, ...) 
>>> session.add(obj) 
>>> session.commit() 
>>> print str(obj) # will print all column=value pairs 

我加強了我的版本的new_bound_classnamed_orm_class多一點與裝飾等,以提供額外的功能,你也可以做到。當然,在封面下,它宣佈一個空的實體類。但不必這樣做,除了這一次。

這會讓你感到厭倦,直到你決定厭倦自己完成所有這些連接,並且爲什麼我無法獲得一個對象屬性,該對象屬性會在我使用它時對相關類進行惰性選擇查詢。 這是,當你跳躍到聲明(或Elixir)。