【问题标题】:sqlalchemy use of inheritance in postgressqlalchemy 在 postgres 中使用继承
【发布时间】:2011-07-21 20:49:15
【问题描述】:

为了学习 sqlalchemy(和 python),我正在尝试复制一个已经存在的项目,但是在使用 postgres 弄清楚 sqlalchemy 和继承时遇到了麻烦。

这是我们的 postgres 数据库所做的一个示例(显然,这是简化的):

CREATE TABLE system (system_id SERIAL PRIMARY KEY, 
                     system_name VARCHAR(24) NOT NULL);
CREATE TABLE file_entry(file_entry_id SERIAL, 
                        file_entry_msg VARCHAR(256) NOT NULL, 
                        file_entry_system_name VARCHAR(24) REFERENCES system(system_name) NOT NULL);
CREATE TABLE ops_file_entry(CONSTRAINT ops_file_entry_id_pkey PRIMARY KEY (file_entry_id), 
     CONSTRAINT ops_system_name_check CHECK ((file_entry_system_name = 'ops'::bpchar))) INHERITS (file_entry);
CREATE TABLE eng_file_entry(CONSTRAINT eng_file_entry_id_pkey PRIMARY KEY (file_entry_id),
     CONSTRAINT eng_system_name_check CHECK ((file_entry_system_name = 'eng'::bpchar)) INHERITS (file_entry);
CREATE INDEX ops_file_entry_index ON ops_file_entry USING btree (file_entry_system_id);
CREATE INDEX eng_file_entry_index ON eng_file_entry USING btree (file_entry_system_id);

然后插入将通过触发器完成,以便将它们正确插入到子数据库中。比如:

CREATE FUNCTION file_entry_insert_trigger() RETURNS "trigger"
    AS $$
DECLARE 
BEGIN
     IF NEW.file_entry_system_name = 'eng' THEN
        INSERT INTO eng_file_entry(file_entry_id, file_entry_msg, file_entry_type, file_entry_system_name) VALUES (NEW.file_entry_id, NEW.file_entry_msg, NEW.file_entry_type, NEW.file_entry_system_name);
     ELSEIF NEW.file_entry_system_name = 'ops' THEN
        INSERT INTO ops_file_entry(file_entry_id, file_entry_msg, file_entry_type, file_entry_system_name) VALUES (NEW.file_entry_id, NEW.file_entry_msg, NEW.file_entry_type, NEW.file_entry_system_name);
     END IF;
     RETURN NULL;
 END;
 $$ LANGUAGE plpgsql;

总之,我有一个父表,其外键指向另一个表。然后我有 2 个子表存在,并且插入是基于给定值完成的。在我上面的示例中,如果 file_entry_system_name 是“ops”,则该行进入 ops_file_entry 表; 'eng' 进入 eng_file_entry_table。我们的生产环境中有数百个子表,考虑到数据量,它确实加快了速度,所以我想保持相同的结构。我可以查询父表,只要我给它正确的“system_name”,它就会立即知道要查看哪个子表。

我的愿望是用 sqlalchemy 来模拟这一点,但我找不到任何详细说明的示例。我通过示例查看了 sqlalchemy 生成的 sql,我可以看出它在数据库端没有做任何类似的事情。

我能想到的最好的方法是:

class System(_Base):
    __tablename__ = 'system'
    system_id = Column(Integer, Sequence('system_id_seq'), primary_key = True)
    system_name = Column(String(24), nullable=False)
    def __init(self, name)
        self.system_name = name
class FileEntry(_Base):
    __tablename__ = 'file_entry'
    file_entry_id = Column(Integer, Sequence('file_entry_id_seq'), primary_key=True)
    file_entry_msg = Column(String(256), nullable=False)
    file_entry_system_name = Column(String(24), nullable=False, ForeignKey('system.system_name'))
    __mapper_args__ = {'polymorphic_on': file_entry_system_name}
    def __init__(self, msg, name)
        self.file_entry_msg = msg
        self.file_entry_system_name = name
class ops_file_entry(FileEntry):
    __tablename__ = 'ops_file_entry'
    ops_file_entry_id = Column(None, ForeignKey('file_entry.file_entry_id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 'ops_file_entry'}

最后,我错过了什么?我如何告诉 sqlalchemy 将插入 FileEntry 的任何内容与系统名称“ops”关联到“ops_file_entry”表?我的理解有问题吗?

对我应该做什么的一些见解会很棒。

【问题讨论】:

标签: python postgresql inheritance sqlalchemy polymorphic-associations


【解决方案1】:

您只需创建一个ops_file_entry 的新实例(不应该是OpsFileEntry 吗?),将其添加到会话中,刷新时,将在表file_entry 和表@ 中插入一行987654324@.

您不需要设置file_entry_system_name 属性,也不需要设置触发器。

【讨论】:

    【解决方案2】:

    我真的不知道 python 或 sqlalchemy,但我想我会为了过去的缘故试一试。 ;)

    您是否尝试过在应用程序级别设置自己的触发器?这样的事情可能会起作用:

    from sqlalchemy import event, orm
    
    def my_after_insert_listener(mapper, connection, target):
        # set up your constraints to store the data as you want
        if target.file_entry_system_name = 'eng'
            # do your child table insert
        elseif target.file_entry_system_name = 'ops'
            # do your child table insert
        #…
    
    mapped_file_entry_class = orm.mapper(FileEntry, 'file_entry')
    # associate the listener function with FileEntry,
    # to execute during the "after_insert" hook
    event.listen(mapped_file_entry_class, 'after_insert', my_after_insert_listener)
    

    我并不肯定,但我认为target(或者mapper)应该包含正在插入的数据。

    Events (esp. after_create)mapper 可能会有所帮助。

    【讨论】:

    • 我正试图摆脱创建触发器。应该有一种方法可以告诉 sqlalchemy 继承如何工作,以及如何处理要插入哪个子表的约束。应该都可以在创建表的声明式方法中完成。感谢您的回复。
    猜你喜欢
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 2015-03-04
    相关资源
    最近更新 更多