"The class already has a specific primary display error" with SQLAlchemy

In October 2010, I sent this question to the Sqlalchemy user list. At that time, I just used the clear_mappers mentioned in the post and did not try to figure out what the problem was. It was very naughty. Today I again ran into this error and decided to build the minimal example that appears below. Michael also addressed what was probably the same problem back in 2006. I decided to continue here to give Michael a break from my dumb questions.

So, the result seems to be that for a given class definition, you cannot define more than one defined one. In my case, I have a Pheno class declared in the module scope (I assume this is the top-level scope here), and every time make_tables is make_tables , it tries to define a different make_tables .

Mike wrote: β€œBased on the description of the problem above, you need to ensure that your Python classes are declared in the same scope as your mappers. The error message you get assumes thatβ€œ Pheno ”is being declared at the module level.” This will take care of the problem, but how can I get around this without changing my current structure? What other options do I have, if any? Apparently, mapper does not have such an option as "if the determinant is already defined, exit without doing anything", which will take care of this beautifully. I think I could define a wrapper function, but that would be pretty ugly.

 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') 

An error message will appear. Tested with SQLAlchemy 0.6.3-3 on Debian compression.

 $ 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. 

EDIT: in the SQLAlchemy documentation : mapper () API , I could replace mapper(Pheno, pheno_table) above with

 from sqlalchemy.orm.exc import UnmappedClassError try: class_mapper(Pheno) except UnmappedClassError: mapper(Pheno, pheno_table) 

If no qualifier is defined for Pheno, it throws an UnmappedClassError . This at least does not return an error in my test script, but I did not check if it really works. Comments?

EDIT2: At the suggestion of Denis, the following works:

 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') 

However superficially similar

 # 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') 

no. I get the same error message as before. I do not understand the review problems very well to say why. Isn't this the Pheno class in both cases in some local area?

+4
source share
2 answers

You are trying to map the same Pheno class to two different tables. SQLAlchemy allows you to use only one primary mapper for each class so that it knows which table to use for session.query(Pheno) . It is not clear what you want to get from your question, so I can not offer a solution. There are two obvious options:

  • define a separate class to display the second table,
  • Create a non-primary mapping for the second table by passing the non_primary=True parameter and passing it (the value returned by the mapper() function) to session.query() instead of the class.

Update: to define a separate class for each table, you can put its definition in 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 
+2
source

I may not quite understand what you want, but this recipe creates the same column in different __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() 

information in sqlalchemy example

0
source

All Articles