2011-05-18 64 views
4

早在2010年10月,我就發佈了this question到Sqlalchemy用戶列表。 當時,我只是使用了郵件中提到的clear_mappers解決方法,並沒有試圖找出問題所在。那對我來說很調皮。今天我再次遇到了這個bug,並決定構建一個最小的例子,如下所示。邁克爾在2006年還提到了probably the same issue。我決定在這裏跟進,讓邁克爾從我愚蠢的問題中解脫出來。「Class has a primary mapper defined」error with SQLAlchemy

因此,結果似乎是給定的類定義,你不能有多個定義的映射器。在我的情況下,我有在模塊範圍內聲明的Pheno類(我假設這是頂級範圍),每次運行make_tables時,它都會嘗試定義另一個映射器。

Mike寫道:「基於上述問題的描述,您需要確保您的Python類與映射器的聲明範圍相同。您收到的錯誤消息表明'Pheno'聲明在模塊中水平。」這會照顧到這個問題,但我如何管理這個問題,而不改變我目前的結構?我有什麼其他選擇,如果有的話?很明顯,mapper沒有像「如果mapper已經被定義,退出而沒有做任何事情」的選項,這將很好地照顧它。我想我可以定義一個包裝函數,但那會非常難看。

from sqlalchemy import * 
from sqlalchemy.orm import * 

def make_pheno_table(meta, schema, name='pheno'): 
    pheno_table = Table(
     name, meta, 
     Column('patientid', String(60), primary_key=True), 
     schema=schema, 
     ) 
    return pheno_table 

class Pheno(object): 
    def __init__(self, patientid): 
     self.patientid = patientid 

def make_tables(schema): 
    from sqlalchemy import MetaData 
    meta = MetaData() 
    pheno_table = make_pheno_table(meta, schema) 
    mapper(Pheno, pheno_table) 
    table_dict = {'metadata': meta, 'pheno_table':pheno_table} 
    return table_dict 

table_dict = make_tables('foo') 
table_dict = make_tables('bar') 

錯誤消息如下。使用SQLAlchemy 0.6.3-3進行Debian壓縮測試。

$ python test.py 
Traceback (most recent call last): 
    File "test.py", line 25, in <module> 
    table_dict = make_tables('bar') 
    File "test.py", line 20, in make_tables 
    mapper(Pheno, pheno_table) 
    File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/__init__.py", line 818, in mapper 
    return Mapper(class_, local_table, *args, **params) 
    File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/mapper.py", line 209, in __init__ 
    self._configure_class_instrumentation() 
    File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/mapper.py", line 381, in _configure_class_instrumentation 
    self.class_) 
sqlalchemy.exc.ArgumentError: Class '<class '__main__.Pheno'>' already has a primary mapper defined. Use non_primary=True to create a non primary Mapper. clear_mappers() will remove *all* current mappers from all classes. 

編輯:每在SQLAlchemy: The mapper() API的文檔,我上面

from sqlalchemy.orm.exc import UnmappedClassError 

try: 
    class_mapper(Pheno) 
except UnmappedClassError: 
    mapper(Pheno, pheno_table) 

如果映射器沒有爲苯氧定義,它拋出一個UnmappedClassError更換mapper(Pheno, pheno_table)。這至少不會在我的測試腳本中返回一個錯誤,但我沒有檢查它是否真正起作用。註釋?

EDIT2:每丹尼斯的建議,以下工作:

class Tables(object): 
    def make_tables(self, schema): 
     class Pheno(object): 
      def __init__(self, patientid): 
       self.patientid = patientid 

     from sqlalchemy import MetaData 
     from sqlalchemy.orm.exc import UnmappedClassError 
     meta = MetaData() 
     pheno_table = make_pheno_table(meta, schema) 
     mapper(Pheno, pheno_table) 
     table_dict = {'metadata': meta, 'pheno_table':pheno_table, 'Pheno':Pheno} 
     return table_dict 

table_dict = Tables().make_tables('foo') 
table_dict = Tables().make_tables('bar') 

然而,表面上類似於

# does not work                                     
class Tables(object): 
    class Pheno(object): 
     def __init__(self, patientid): 
      self.patientid = patientid 

    def make_tables(self, schema): 
     from sqlalchemy import MetaData 
     from sqlalchemy.orm.exc import UnmappedClassError 
     meta = MetaData() 
     pheno_table = make_pheno_table(meta, schema) 
     mapper(self.Pheno, pheno_table) 
     table_dict = {'metadata': meta, 'pheno_table':pheno_table, 'Pheno':self.Pheno} 
     return table_dict 

table_dict = Tables().make_tables('foo') 
table_dict = Tables().make_tables('bar') 

沒有。我收到了和以前一樣的錯誤信息。 我不太瞭解範圍問題,足以說明原因。 是不是Pheno類在兩種情況下在某種局部範圍?

回答

2

您正試圖將同一類Pheno映射到2個不同的表。 SQLAlchemy僅允許每個類使用一個主映射器,以便它知道要使用哪個表session.query(Pheno)。目前還不清楚你希望從你的問題中得到什麼,所以我不能提出解決方案。有2級明顯的選項:

  • 定義單獨的類映射到第二表,
  • 通過傳遞non_primary=True參數創建non-primary mapper爲第二表並把它傳遞(由mapper()函數的返回值),以session.query()代替類。

更新:定義單獨的類中的每個表,你可以把它定義爲make_tables()

def make_tables(schema): 
    from sqlalchemy import MetaData 
    meta = MetaData() 
    pheno_table = make_pheno_table(meta, schema) 
    class Pheno(object): 
     def __init__(self, patientid): 
      self.patientid = patientid  
    mapper(Pheno, pheno_table) 
    table_dict = {'metadata': meta, 
        'pheno_class': Pheno, 
        'pheno_table':pheno_table} 
    return table_dict 
+0

謝謝你的明確解釋。你能否建議如何「定義單獨的類映射到第二個表」,而不改變我的代碼的結構? – 2011-05-19 10:01:01

+0

謝謝。我不知道有可能在函數內定義一個類。我花了一些時間試圖找到這個昨天的文件,沒有成功。你能提供一個參考嗎?實際上,我的代碼中'make_tables'的實際版本是在一個類 - 類的Tables(object):... def make_tables(schema)...'中。在那裏定義類是否有意義? – 2011-05-19 11:23:27

0

也許我不太明白你想要什麼,但這個配方創建相同的列在不同的__tablename__

class TBase(object): 
    """Base class is a 'mixin'. 
    Guidelines for declarative mixins is at: 

    http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixin-classes 

    """ 
    id = Column(Integer, primary_key=True) 
    data = Column(String(50)) 

    def __repr__(self): 
     return "%s(data=%r)" % (
      self.__class__.__name__, self.data 
     ) 

class T1Foo(TBase, Base): 
    __tablename__ = 't1' 

class T2Foo(TBase, Base): 
    __tablename__ = 't2' 

engine = create_engine('sqlite:///foo.db', echo=True) 

Base.metadata.create_all(engine) 

sess = sessionmaker(engine)() 

sess.add_all([T1Foo(data='t1'), T1Foo(data='t2'), T2Foo(data='t3'), 
     T1Foo(data='t4')]) 

print sess.query(T1Foo).all() 
print sess.query(T2Foo).all() 
sess.commit() 

info in example sqlalchemy