【问题标题】:Flask-Sqlalchemy: Table with 3 primary keys that are also foreign keysFlask-Sqlalchemy:具有 3 个主键的表,它们也是外键
【发布时间】:2019-12-25 20:45:46
【问题描述】:

我正在尝试将表从纯 SQL 转换为 Flask-Sqlalchemy,但可用的文档并不清楚如何执行此特定场景 - 主键也是外键。

建表的 SQL 如下,运行良好:

CREATE TABLE IF NOT EXISTS `ws`.`Perfil_Plano_Transacao` (
    `pptr_perf_id` INT NOT NULL,
    `pptr_tran_id` INT NOT NULL,
    `pptr_plan_id` INT NOT NULL,
    `pptr_dt_incluscao` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `pptr_dt_atualizacao` TIMESTAMP NULL,
PRIMARY KEY (`pptr_perf_id`, `pptr_tran_id`, `pptr_plan_id`),
INDEX `fk_Perfil_Plano_Transacao_Transacao1_idx` (`pptr_tran_id` ASC),
INDEX `fk_Perfil_Plano_Transacao_Plano1_idx` (`pptr_plan_id` ASC),
CONSTRAINT `fk_Perfil_Plano_Transacao_Perfil1`
    FOREIGN KEY (`pptr_perf_id`)
    REFERENCES `ws`.`Perfil` (`perf_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
CONSTRAINT `fk_Perfil_Plano_Transacao_Transacao1`
    FOREIGN KEY (`pptr_tran_id`)
    REFERENCES `ws`.`Transacao` (`tran_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
CONSTRAINT `fk_Perfil_Plano_Transacao_Plano1`
    FOREIGN KEY (`pptr_plan_id`)
    REFERENCES `ws`.`Plano` (`plan_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB

直到这里我得到的 Python 代码是:

class PerfilPlanoTransacaoModel(db.Model):

    __tablename__ = 'perfil_plano_transacao'

    pptr_perf_id = db.Column(db.Integer, primary_key=True, autoincrement=False)
    pptr_plan_id = db.Column(db.Integer, primary_key=True, autoincrement=False)
    pptr_tran_id = db.Column(db.Integer, primary_key=True, autoincrement=False)
    pptr_dt_inclusao = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    pptr_dt_atualizacao = db.Column(db.DateTime, nullable=True)

    __table_args__ = (
        db.ForeignKeyConstraint(
            ['pptr_perf_id', 'pptr_plan_id', 'pptr_tran_id'],
            ['perfil.perf_id', 'plano.plan_id', 'transacao.tran_id'],
            ['fk_Perfil_Plano_Transacao_Perfil1', 'fk_Perfil_Plano_Transacao_Plano1', 'fk_Perfil_Plano_Transacao_Transacao1']
        ),
    )

我想知道我是否朝着正确的方向前进。例如,我没有找到如何声明外键约束的name 以及如何设置INDEX。有没有更多Flaks-Sqlalchemy 的方式来完成这一切?

【问题讨论】:

  • 为什么会有多个 primary_key?为什么会这样?我想你想要一个foreingKey和一个关系字段
  • @NotSoShabby 因为在这种情况下,主键由其他 3 个表(Role、Plan 和 Transaction)的主键组成。这是一个非常常见且已知的用例。在这种情况下,此表用于向我提供配置文件在特定计划下可以进行的交易。例如:如果您支付了plan type A,并且您是vip角色,您可以进行交易X和Y,但如果您是普通角色,您可以使用交易Y。
  • 其他外键不是属于其他表吗?我很确定每个表只能有一个主键。你遇到了什么错误?
  • @NotSoShabby 是的,它们属于外键预期的其他表:-)。在 SQL 上,有一个组合的主键是很常见的,并且提供的 SQL 工作正常。关键是我在 Flask-Sqlalchemy 中找不到这种特定呈现场景的示例。我还没有收到任何错误,因为我没有尝试运行代码。我正在努力寻找更好的示例,以确保它能够按预期工作,而不仅仅是工作。

标签: python sqlalchemy foreign-keys flask-sqlalchemy composite-primary-key


【解决方案1】:

@Halvor 的答案是正确的,但我只是补充一点,您有一个复合主键但没有复合外键,您有三个指向不同表的单列外键,这意味着您可以声明外键在您的列定义中:

from sqlalchemy import ForeignKey

class PerfilPlanoTransacaoModel(db.Model):

    __tablename__ = 'perfil_plano_transacao'

    pptr_perf_id = db.Column(
        db.Integer, 
        ForeignKey('perfil.perf_id'),
        primary_key=True, 
        autoincrement=False,
    )
    pptr_plan_id = db.Column(
        db.Integer, 
        ForeignKey('plano.plan_id'),
        primary_key=True, 
        autoincrement=False,
    )
    pptr_tran_id = db.Column(
        db.Integer, 
        ForeignKey('transacao.tran_id'),
        primary_key=True, 
        autoincrement=False,
    )
    pptr_dt_inclusao = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    pptr_dt_atualizacao = db.Column(db.DateTime, nullable=True)

使用ForeignKeyConstraint 比使用ForeignKey 稍微冗长一些,并且我们在此示例中在列定义中创建的ForeignKey 对象最终会转换为ForeignKeyConstraint 对象,只是更容易使用@ 987654326@ 处理单列键时的对象。 __table_args__ 中定义的 ForeignKeyConstraint 对象通常仅在您需要创建复合外键时直接使用,例如,如果您有另一个要引用 perfil_plano_transacao 的表,则它需要是复合外键并且你必须像上面那样定义它。

我将听从@Halvor 对您其余问题的回答。

【讨论】:

  • 非常感谢您的回答。我肯定会同时使用两者(你的和@Halvor),因为你的贡献显示了图书馆的高水平使用,但接受的答案正是我正在寻找的答案。
【解决方案2】:

如何声明外键约束的name

可以通过在__table_args__ 中添加多个ForeignKeyConstraints 来添加多个外键约束。例如:

__table_args__ = (
    ForeignKeyConstraint(['pptr_perf_id'], ['perfil.perf_id'], name='fk_Perfil_Plano_Transacao_Perfil1'),
    ForeignKeyConstraint(['pptr_plan_id'], ['plano.plan_id'], name='fk_Perfil_Plano_Transacao_Plano1'),
    ForeignKeyConstraint(['pptr_tran_id'], ['transacao.tran_id'], name='fk_Perfil_Plano_Transacao_Transacao1'),
)

在这里您看到您定义了本地列,然后是原始表中的列并为其命名。前两个参数是数组的原因是允许复合外键。

对您的代码进行此更改应评估为以下查询:

CREATE TABLE perfil_plano_transacao (
        pptr_perf_id INTEGER NOT NULL,
        pptr_plan_id INTEGER NOT NULL,
        pptr_tran_id INTEGER NOT NULL,
        pptr_dt_inclusao DATETIME NOT NULL,
        pptr_dt_atualizacao DATETIME,
        PRIMARY KEY (pptr_perf_id, pptr_plan_id, pptr_tran_id),
        CONSTRAINT "fk_Perfil_Plano_Transacao_Perfil1" FOREIGN KEY(pptr_perf_id) REFERENCES perfil (perf_id),
        CONSTRAINT "fk_Perfil_Plano_Transacao_Plano1" FOREIGN KEY(pptr_plan_id) REFERENCES plano (plan_id),
        CONSTRAINT "fk_Perfil_Plano_Transacao_Transacao1" FOREIGN KEY(pptr_tran_id) REFERENCES transacao (tran_id)
)

如何设置INDEX

添加索引的简单方法是在列声明中设置它:

pptr_perf_id = Column(Integer, primary_key=True, autoincrement=False)
pptr_plan_id = Column(Integer, primary_key=True, autoincrement=False, index=True)
pptr_tran_id = Column(Integer, primary_key=True, autoincrement=False, index=True)

这将导致以下两个查询:

CREATE INDEX ix_perfil_plano_transacao_pptr_plan_id ON perfil_plano_transacao (pptr_plan_id)
CREATE INDEX ix_perfil_plano_transacao_pptr_tran_id ON perfil_plano_transacao (pptr_tran_id)

也可以在表声明后单独添加:

from sqlalchemy import Index
Index('fk_Perfil_Plano_Transacao_Transacao1_idx', PerfilPlanoTransacaoModel.pptr_tran_id.asc())
Index('fk_Perfil_Plano_Transacao_Plano1_idx', PerfilPlanoTransacaoModel.pptr_plan_id.asc())

这将导致以下两个查询:

CREATE INDEX "fk_Perfil_Plano_Transacao_Transacao1_idx" ON perfil_plano_transacao (pptr_tran_id ASC)
CREATE INDEX "fk_Perfil_Plano_Transacao_Plano1_idx" ON perfil_plano_transacao (pptr_plan_id ASC)

【讨论】:

    猜你喜欢
    • 2020-03-09
    • 2019-09-15
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 2017-05-24
    • 2017-08-28
    相关资源
    最近更新 更多