如何使用 sqlalchemy declarative base 多層次繼承

華科雲商小雪發表於2024-02-06

在SQLAlchemy中,透過 declarative_base 建立的基類可以透過多層次的繼承建立繼承關係。這允許你在資料庫中建立具有繼承結構的表。在我使用某資料庫做中轉的時候,經常會遇到各種各樣的問題,例如下面的問題,透過記錄並附上完美的解決方案,以便為查詢更加方便。

問題背景

在使用 sqlalchemy declarative base 建立模型時,我有許多具有相同列的表。不同的只是表名。我想設定一個繼承鏈來最小化程式碼重複。以下單層繼承按我想要的方式工作:


from 
sqlalchemy 
import 
Column, 
Integer, 
Text

from sqlalchemy. ext. declarative import declarative_base, declared_attr
from sqlalchemy. orm import sessionmaker

engine = sqlalchemy. create_engine( 'sqlite:///monDom5.db')


class Base( object):
    """base for all table classes"""
    __abstract__ = True
    __table_args__ = { 'autoload': True, 'autoload_with': engine}
    @declared_attr
    def __tablename__( cls):
        return cls. __name__. lower()


Base = declarative_base( cls = Base)


class TransMap_HgmIntronVector( Base):
    AlignmentId = Column( Text, primary_key = True)

但是需要為每個 Hgm 基類的例項指定 AlignmentId 列。相反,我想這樣做,但在嘗試實際使用它時出現 sqlalchemy.exc.InvalidRequestError 錯誤:


from 
sqlalchemy 
import 
Column, 
Integer, 
Text

from sqlalchemy. ext. declarative import declarative_base, declared_attr
from sqlalchemy. orm import sessionmaker

engine = sqlalchemy. create_engine( 'sqlite:///monDom5.db')

class Base( object):
    """base for all table classes"""
    __abstract__ = True
    __table_args__ = { 'autoload': True, 'autoload_with': engine}
    @declared_attr
    def __tablename__( cls):
        return cls. __name__. lower()


# model for all Hgm tables

class Hgm( Base):
    __abstract__ = True
    AlignmentId = Column( Text, primary_key = True)


Base = declarative_base( cls = Hgm)

class TransMap_HgmIntronVector( Hgm):
    pass



metadata = Base. metadata
Session = sessionmaker( bind = engine)
session = Session()

導致錯誤

>>> metadata = Base.metadata

>>> Session = sessionmaker(bind=engine)
>>> session = Session()
>>> session.query(TransMap_HgmIntronVector).all()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/cluster/home/ifiddes/anaconda2/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1260, in query
   return self._query_cls(entities, self, **kwargs)
 File "/cluster/home/ifiddes/anaconda2/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 110, in __init__
   self._set_entities(entities)
 File "/cluster/home/ifiddes/anaconda2/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 118, in _set_entities
   entity_wrapper(self, ent)
 File "/cluster/home/ifiddes/anaconda2/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 3829, in __init__
   "expected - got '%r'" % (column, )
sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got '<class '__main__.TransMap_HgmIntronVector'>'

解決方案

官方檔案中提供了一個示例。特別地, __abstract__ = True 是沒有必要的。這樣可以正常工作:


class 
Base(
object):

    @declared_attr
    def __tablename__( cls):
        return cls. __name__. lower()

class Hgm( Base):
    AlignmentId = Column( Text, primary_key = True)

Base = declarative_base( cls = Hgm)

class TransMap_HgmIntronVector( Base):
    pass

請注意,使用混合類來代替具有相同列可能更簡單。

透過上面的詳細步驟可以知道 Person 類是基類, Employee 類繼承自 Person Manager 類繼承自 Employee 。每個類都對應一個資料庫表,並且在子類中透過 ForeignKey 建立了關聯。透過多層次繼承,你可以在資料庫中建立類似於類的繼承關係的表結構。

所以說看是結構複雜,其實是非常的簡單的,需要注意的是一些混合類來代替具有相同列的問題,所以說,大家如果有不懂的可以評論區留言討論。


來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70034537/viewspace-3006427/,如需轉載,請註明出處,否則將追究法律責任。

相關文章