使用SQLAlchemy处理Postgres继承

12 投票
2 回答
3857 浏览
提问于 2025-04-17 00:14

我有一个关于如何使用SQLAlchemy创建PostgreSQL表继承的问题。

我有这两个表:

CREATE TABLE his
(
  idg integer,
  idfk integer,
  idh integer NOT NULL defautl nextval('his_seq'),
  "type" character varying,
  CONSTRAINT __his_pkey PRIMARY KEY (idh)
);
CREATE TABLE data
(
  "text" character varying,
)
INHERITS (his);

在执行任何DDL命令之前,我写了这段Python代码:

from sqlalchemy  import *
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import event

Base = declarative_base()

class His(Base):
    __tablename__ = 'his'

    idg = Column(Integer())
    idfk = Column(Integer())
    idh = Column(Integer(), Sequence('his_seq',  start=1,  increment=1),  primary_key=True)
    type= Column(String())

    __mapper_args__ = {'polymorphic_on': type}
    __table_args__ = {'implicit_returning':False}

    def __init__(self,  idg,  idfk,  type):
        self.idg = idg
        self.idfk = idfk
        self.type = type

class Data(His):
    __tablename__ = None
#    __mapper_args__ = {'polymorphic_identity': 'data',  'concrete':True}
    __mapper_args__ = {'polymorphic_identity': 'data'}
    text = Column(String())

    def __init__(self, text):
        self.text = text

@event.listens_for(His.__table__,  'after_create')
def create_child_tables(target, connection,  **kw):   
    connection.execute("""
        CREATE TABLE data(
        ) INHERITS (his)
    """)

    connection.execute("""
        CREATE OR REPLACE FUNCTION his_insert_trigger()
        RETURNS TRIGGER AS $$
        BEGIN
            IF (NEW.type='data') THEN
                INSERT INTO data VALUES (NEW.*);
            ELSE
                RAISE EXCEPTION 'Table type is unknown for historical porpurses.';
            END IF;
        RETURN NULL;
        END;
        $$
        LANGUAGE plpgsql;    
    """)

    connection.execute("""
        CREATE TRIGGER his_insert
        BEFORE INSERT ON his
        FOR EACH ROW EXECUTE PROCEDURE his_insert_trigger();
    """)

@event.listens_for(His.__table__, "before_drop")
def create_child_tables(target, connection, **kw):
    connection.execute("drop table data")
    connection.execute("drop table his")
    connection.execute("drop sequence his_seq")

e = create_engine('postgresql://localhost:5433/des', echo=True)
#Base.metadata.drop_all(e)
Base.metadata.create_all(e)
s = Session(e)

s.add_all([
    Data('hola'), 
    Data('pedorrete'), 
    Data('pedorrete2')
])

s.commit()
s.close()

这个例子(就像在http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PostgreSQLInheritance中解释的那样)创建了两个表,但SQLAlchemy总是使用它自己的表来插入数据记录,这些记录被插入到“data”表和“his”表中,而“data”表中的“text”字段实际上是创建在“his”表里的。

那么,有没有办法告诉SQLAlchemy,“data”表必须从“his”表继承,并且必须在其中添加“text”字段,并且在我向“data”表插入任何记录时,必须使用“data”而不是“his”呢?

谢谢。

2 个回答

1

Sqlalchemy的表继承功能是希望你的子表只包含额外的数据,它会自己处理表之间的连接,以获取合并后的信息。如果你打算使用PostgreSQL自带的表继承功能,其实就不需要使用sqlalchemy的继承特性,你只需把它们当作不相关的表来处理,因为Postgres会为你完成这些工作。

为了避免重复代码,你可以把列的定义放到一个独立的类中这个类不需要继承Base,然后让所有的表类去继承这个独立的类。


class HisCols:
    idg = Column(Integer())
    idfk = Column(Integer())
    idh = Column(Integer(), Sequence('his_seq',  start=1,  increment=1), primary_key=True)
    type = Column(String())


class His(Base, HisCols):
    __tablename__ = 'his'
    __table_args__ = {'implicit_returning':False}


class Data(Base, HisCols):
    __tablename__ = 'data'
    text = Column(String())

    # And if you really need it (you probably don't) you can add a relationship to the parent table
    idh = Column(Integer(), ForeignKey('his.idh'), Sequence('his_seq',  start=1,  increment=1),  primary_key=True)
    parent = relationship(His)

2

SQLAlchemy尽量做到跨平台,所以它不支持很多Postgres特有的功能。这里有一个来自SQLAlchemy开发者的回答,针对一个遇到类似问题的人:

http://www.mail-archive.com/sqlalchemy@googlegroups.com/msg17443.html

撰写回答