SQLAlchemyで発生する「Class already has a primary mapper defined」エラーとその解決方法
Class already has a primary mapper defined
エラーは、SQLAlchemyでdeclarative
を使用しているときに発生するエラーです。これは、エンティティクラスに対してすでにプライマリマッパーが定義されている場合に発生します。
原因
このエラーが発生する理由は、エンティティクラスに対して複数のプライマリマッパーが定義されているためです。これは、以下のいずれかの理由で発生する可能性があります。
- エンティティクラスに対して
@declarative
デコレータを複数回適用している。 - エンティティクラスを継承している親クラスと子クラスの両方で
@declarative
デコレータを適用している。 - エンティティクラスに対して
mapper()
関数を直接呼び出している。
解決方法
このエラーを解決するには、以下のいずれかの方法を試してください。
例
以下の例は、Class already has a primary mapper defined
エラーが発生するコード例と、その解決方法を示しています。
例1:@declarative
デコレータを複数回適用する
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
@declarative_base()
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
address = Column(String)
このコードでは、User
クラスとAddress
クラスの両方に@declarative
デコレータを適用しています。そのため、Address
クラスに対してClass already has a primary mapper defined
エラーが発生します。
このエラーを解決するには、Address
クラスから@declarative
デコレータを削除します。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
address = Column(String)
例2:エンティティクラスを継承している場合
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String)
class User(Person):
__tablename__ = 'users'
email = Column(String)
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String)
class User(Person):
__tablename__ = 'users'
email = Column(String)
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
@declarative_base()
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
address = Column(String)
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
address = Column(String)
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String)
class User(Person):
__tablename__ = 'users'
email = Column(String)
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
name = Column(String)
class User(Person):
__tablename__ = 'users'
email = Column(String)
declarative
を使用せずに、mapper()
関数を直接呼び出すことで、エンティティクラスのマッパーを明示的に定義することができます。
from sqlalchemy import Column, Integer, String, MetaData, Table
from sqlalchemy.orm import mapper
metadata = MetaData()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String)
)
class User(object):
pass
mapper(User, users_table)
このコードでは、declarative
を使用せずに、User
クラスのマッパーを明示的に定義しています。
@declared_attrデコレータを使用する
@declared_attr
デコレータを使用することで、エンティティクラスの属性に対して動的にマッパーを定義することができます。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base, declared_attr
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
@declared_attr
def name(cls):
return Column(String)
このコードでは、@declared_attr
デコレータを使用して、User
クラスのname
属性に対して動的にマッパーを定義しています。
relationship()関数を直接呼び出す
relationship()
関数を直接呼び出すことで、エンティティクラス間のリレーションシップを明示的に定義することができます。
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
class User(object):
id = Column(Integer, primary_key=True)
name = Column(String)
class Address(object):
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
address = Column(String)
user_addresses = relationship('Address', backref='user')
このコードでは、relationship()
関数を直接呼び出すことで、User
クラスとAddress
クラス間のリレーションシップを明示的に定義しています。
sqlalchemy