Hi, I'm belatedly following up on this earlier thread from October 2010. I decided to go back to it and figure out what the problem was. So, I created a working minimal example. I posted on StackOverflow, but it doesn't seem to have attracted much interest, so I'm copying it here. The SO link is http://stackoverflow.com/questions/6051674/class-already-has-a-primary-mapper-defined-error-with-sqlalchemy I propose a possible solution at the end of the post, but I'd like to know if it meets with the approval of the cognoscenti. I'll copy the post below, In case SO falls off the net or something. Please CC me on any reply. Thanks.
********************************************************************* Back in October 2010, I posted this question to the Sqlalchemy user list. At the time, I just used the clear_mappers workaround mentioned in the message, and didn't try to figure out what the problem was. That was very naughty of me. Today I ran into this bug again, and decided to construct a minimal example, which appears below. Michael also addressed what is probably the same issue back in 2006. I decided to follow up here, to give Michael a break from my dumb questions. So, the upshot appears to be that for a given class definition, you can't have more than one mapper defined. In my case I have the Pheno class declared in module scope (I assume that is top level scope here) and each time make_tables runs, it tries to define another mapper. Mike wrote "Based on the description of the problem above, you need to ensure your Python classes are declared in the same scope as your mappers. The error message you're getting suggests that 'Pheno' is declared at the module level." That would take care of the problem, but how do I manage that, without altering my current structure? What other options do I have, if any? Apparently mapper doesn't have an option like "if the mapper is already defined, exit without doing anything", which would take care of it nicely. I guess 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') ********************************************************************* Error message follows. Tested with SQLAlchemy 0.6.3-3 on Debian squeeze. $ 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 t ********************************************************************* EDIT: Per the documentation in SQLAlchemy: The 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 a mapper is not defined for Pheno, it throws an UnmappedClassError. This at least doesn't return an error in my test script, but I haven't checked if it actually works. Comments? On Mon, 18 Oct 2010 13:06:01 -0400, Michael Bayer <mike...@zzzcomputing.com> wrote: > On Oct 18, 2010, at 5:36 AM, Faheem Mitha wrote: >> Hi, >> I should say in advance that I don't have a reproduction script for >> this, though I could probably manufacture one if necessary. >> I have a python script which calls a function repeatedly. Each time >> the function is called, it creates a bunch of SQLA tables, classes >> and mappers between them. I'd be perfectly happy to remove these >> after each invocation, but I don't know how, and apparently they >> stick around. I'd hope that these objects all get >> overwritten. Since the classes are just regular Python classes, I >> know these do get overwritten. I'm not sure about the Table >> objects, and it definitely appears that the mappers don't get >> cleanly overwritten, judging by the error message I get (complete >> traceback at the bottom of the message) >> sqlalchemy.exc.ArgumentError: Class '<class 'dbschema.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. >> So, I want to >> a) confirm that the Table objects get overwritten (they contain >> state data, correct?) and I don't want the Table objects from a >> previous invocation to get mixed up with one from a later >> invocation. > Table objects don't get "overwritten". They are created exactly > once for a specific MetaData object, and an error is raised if you > attempt to make a new Table of the same name within the same > MetaData object. If you'd like to make all new tables, use a new > MetaData object. As far as "magic", we are talking here about a > dictionary of name->Table object where a check for "if name in > dictionary: raise error" is performed for the MetaData object in > question. >> and ask >> b) why aren't the mappers overwritten? > Similarly, mappers don't "overwrite" previously existing mappers on > a certain class. They're a one-time operation on a specific class > until clear_mappers() is called. Based on the description of the > problem above, you need to ensure your Python classes are declared > in the same scope as your mappers. The error message you're getting > suggests that 'Pheno' is declared at the module level. Again, as > far as "magic", we are talking about a dictionary of class->mapper > object where a check for "if class in dictionary: raise error" is > performed when mapper() is called for a particular class. >> I'm using clear_mappers as a temporary measure. as suggested by the >> error message, but I don't know if this the right approach. It does >> get rid of the error message. Clarifications appreciated. I >> apologise if this message comes across as "computationally >> illiterate" but despite using SQLA for nearly two years (has it >> been so long?), I still have little understanding of how some of >> it's magic is accomplished. Thanks! >> Regards, Faheem >> >> *********************************************************************** >> traceback follows >> *********************************************************************** >> >> Traceback (most recent call last): >> File "load_all_datasets.py", line 58, in <module> >> load_all_datasets(dbname, dbtype, options.test, options.shard, >> options.recreate, options.jobs) >> File "/home/faheem/snppy/utils.py", line 6, in wrapper >> res = func(*args, **kwargs) >> File "load_all_datasets.py", line 39, in load_all_datasets >> load_dataset(schema, dbname, options.alleletype, options.jobs, >> options.recreate, options.shard, options.test) >> File "/home/faheem/snppy/load_dataset.py", line 71, in load_dataset >> p = platform.Single_Illumina(phenofiles, annotfile, genofile, >> genotablefile, newschema, alleletype, dbtype, dbstring_dbname, jobs) >> File "/home/faheem/snppy/platform.py", line 130, in __init__ >> table_dict = make_tables_illumina(schema) >> File "/home/faheem/snppy/dbschema.py", line 293, in make_tables >> 'race':relation(Race, backref='patients'), >> File "/usr/lib/pymodules/python2.5/sqlalchemy/orm/__init__.py", line 751, >> in mapper >> return Mapper(class_, local_table, *args, **params) >> File "/usr/lib/pymodules/python2.5/sqlalchemy/orm/mapper.py", line 197, in >> __init__ >> self._configure_class_instrumentation() >> File "/usr/lib/pymodules/python2.5/sqlalchemy/orm/mapper.py", line 349, in >> _configure_class_instrumentation >> self.class_) >> sqlalchemy.exc.ArgumentError: Class '<class 'dbschema.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. -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.