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.

Reply via email to