SQLAlchemy 中的复杂外键约束

2024-04-11

我有两张桌子,SystemVariables and VariableOptions. SystemVariables应该是不言自明的,并且VariableOptions包含所有变量的所有可能选择。

VariableOptions有一个外键,variable_id,它说明了它是哪个变量的选项。SystemVariables有一个外键,choice_id,它表明当前选择的是哪个选项。

我已经使用绕过了循环关系use_alter on choice_id, and post_update on SystemVariables' choice关系。但是,我想添加一个额外的数据库约束,以确保choice_id是有效的(即它引用了一个返回它的选项)。

我需要的逻辑,假设sysVar代表行中的SystemVariables表,基本上是:

VariableOptions[sysVar.choice_id].variable_id == sysVar.id

但我不知道如何使用 SQL、声明性或任何其他方法来构造这种约束。如果有必要,我可以在应用程序级别验证这一点,但如果可能的话,我希望在数据库级别验证它。我正在使用 Postgres 9.1。

这可能吗?


你可以实现它没有肮脏的伎俩. Just 扩展外键引用所选选项以包含variable_id此外choice_id.

这是一个工作演示。临时表,这样你就可以轻松地使用它:

CREATE TABLE systemvariables (
  variable_id int PRIMARY KEY
, choice_id   int
, variable    text
);
   
INSERT INTO systemvariables(variable_id, variable) VALUES
  (1, 'var1')
, (2, 'var2')
, (3, 'var3')
;

CREATE TABLE variableoptions (
  option_id   int PRIMARY KEY
, variable_id int REFERENCES systemvariables ON UPDATE CASCADE ON DELETE CASCADE
, option      text
, UNIQUE (option_id, variable_id)  -- needed for the FK
);

ALTER TABLE systemvariables
  ADD CONSTRAINT systemvariables_choice_id_fk
  FOREIGN KEY (choice_id, variable_id) REFERENCES variableoptions(option_id, variable_id);

INSERT INTO variableoptions  VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3)
;

允许选择关联选项:

UPDATE systemvariables SET choice_id = 2 WHERE variable_id = 1;
UPDATE systemvariables SET choice_id = 5 WHERE variable_id = 2;
UPDATE systemvariables SET choice_id = 6 WHERE variable_id = 3;

但没有什么出格的地方:

UPDATE systemvariables SET choice_id = 7 WHERE variable_id = 3;
UPDATE systemvariables SET choice_id = 4 WHERE variable_id = 1;
ERROR:  insert or update on table "systemvariables" violates foreign key constraint "systemvariables_choice_id_fk"
DETAIL: Key (choice_id,variable_id)=(4,1) is not present in table "variableoptions".

正是您想要的。

所有关键列NOT NULL

我想我在后面的答案中找到了更好的解决方案:

  • 如何处理相互依赖的插入 https://stackoverflow.com/questions/24813000/how-to-deal-with-mutually-dependent-inserts/24816197#24816197

解决@ypercube 在评论中提出的问题 https://stackoverflow.com/questions/8394177/complex-foreign-key-constraint-in-sqlalchemy/8395021#comment10383559_8395021,为了避免具有未知关联的条目,请使所有关键列NOT NULL,包括外键。

循环依赖通常会使这变得不可能。这就是经典鸡蛋问题:两者之一必须先出现才能产生另一个。但大自然找到了解决办法,Postgres 也是如此:可延迟的外键约束.

CREATE TABLE systemvariables (
  variable_id int PRIMARY KEY
, variable    text
, choice_id   int NOT NULL
);

CREATE TABLE variableoptions (
  option_id   int PRIMARY KEY
, option      text
, variable_id int NOT NULL REFERENCES systemvariables
     ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
, UNIQUE (option_id, variable_id) -- needed for the foreign key
);

ALTER TABLE systemvariables
ADD CONSTRAINT systemvariables_choice_id_fk FOREIGN KEY (choice_id, variable_id)
   REFERENCES variableoptions(option_id, variable_id) DEFERRABLE INITIALLY DEFERRED; -- no CASCADING here!

New变量和关联选项必须插入到同一事务中:

BEGIN;

INSERT INTO systemvariables (variable_id, variable, choice_id)
VALUES
  (1, 'var1', 2)
, (2, 'var2', 5)
, (3, 'var3', 6);

INSERT INTO variableoptions (option_id, option, variable_id)
VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3);

END;

The NOT NULL约束不能推迟,而是立即强制执行。但外键约束can,因为我们就是这样定义的。交易结束时进行检查,避免了先有鸡还是先有蛋的问题。

In this edited设想,两个外键都被延迟。您可以按任意顺序输入变量和选项。
如果您在两个表中输入相关条目,您甚至可以使其与普通的不可延迟 FK 约束一起使用一份声明使用 CTE,详细信息请参见链接的答案 https://stackoverflow.com/a/24816197/939860.

您可能已经注意到第一个外键约束没有CASCADE修饰符。 (允许更改是没有意义的variableoptions.variable_id级联回来。

另一方面,第二个外键有一个CASCADE修饰符并被定义DEFERRABLE尽管如此。这有一些限制。手册 https://www.postgresql.org/docs/current/sql-createtable.html:

参考动作以外的动作NO ACTION支票不能延期, 即使约束被声明为可延迟的。

NO ACTION是默认值。

因此,引用完整性检查INSERT被推迟,但宣布的级联行动DELETE and UPDATE不是。在 PostgreSQL 9.0 或更高版本中不允许执行以下操作,因为在每个语句之后都会强制执行约束:

UPDATE option SET var_id = 4 WHERE var_id = 5;
DELETE FROM var WHERE var_id = 5;

Details:

  • 定义 DEFERRABLE INITIALLY IMMEDIATE 的约束仍然是 DEFERRED? https://stackoverflow.com/questions/10032272/constraint-defined-deferrable-initially-immediate-is-still-deferred
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SQLAlchemy 中的复杂外键约束 的相关文章

随机推荐

  • Rails 4:具有 has_many 的 CanCanCan 功能:通过关联

    我有一个包含以下模型的 Rails 应用程序 class User lt ActiveRecord Base has many administrations has many calendars through administratio
  • 无法使用 SSH 连接到 docker 容器 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 这个问题似乎不是关于主要由程序员使用的特定编程问题 软件算法或软件工具 help on topic 如果您认为该问题与主题相关另一个 St
  • Zurb 基金会和 IE 8

    下面的 css 示例适用于除 IE8 之外的所有受支持的浏览器 不关注任何低于 IE 8 的浏览器 我正在使用最新版本的 Foundation 当列应该浮动时 它们会在 IE8 中堆叠 实际看到float left 属性应用于样式但它们不浮
  • 在 Mongoose 字符串键中存储 Json 对象

    在我的 Mongoose 架构中 我有一个字符串字段 我希望能够在其中存储 JSON 对象 是否可以 在 Postgres 中 可以将字典存储在字符串列中 我想这样做是因为字典 实际上是 JS 中的 JSON 对象 只是一个简单的读写值 不
  • DatePicker 编辑器模板

    下面是一个 EditorTemplate 它使用以下命令呈现 Bootstrap 日期时间选择器EditorFor助手 我看到的问题是脚本部分 对于一个人来说效果还不错DateTimePicker每个视图 但由于我使用类选择器 每当我使用
  • 如何限制我网站的 API 用户?

    我网站的合法用户偶尔会使用 API 请求攻击服务器 从而导致不良结果 我想设定一个限制 即每 5 秒不超过一次 API 调用或每分钟 n 次调用 尚未弄清楚确切的限制 显然 我可以将每个 API 调用记录在数据库中 并对每个请求进行计算 看
  • 来自终端的 Linux powershell 命令返回命令未找到

    我有一个运行 Ubuntu Server 16 04 且带有 Powershell 的 Linux 机器 如果我从 Linux 终端运行 sudo powershell Invoke Webrequest http ipinfo io js
  • 如何测试在 VB.net TabControl 中选择了哪个选项卡

    我有一个带有两个 TabPage 的 TabControl 我想知道测试当前显示哪个选项卡的最佳方法是什么 我不知道为什么我无法弄清楚这一点 TabControl SelectedTab http msdn microsoft com en
  • F# 查询串联

    我在用SqlDataConnectionF 中的数据提供程序迁移一些行 迁移的一部分是在 3 个表之间进行连接 如下所示 将其视为表的继承A B C where B and C继承自A所以我需要得到的是 类似 Linq Bs Join As
  • 为什么非自定义标头包含在 Access-Control-Request-Headers 中?

    我正在尝试发送跨源请求 就Access Control Request Headers就我而言 我在 FireFox Chrome 和 Safari 中遇到了不同的行为 Chrome Access Control Request Heade
  • 如何在 Netbeans 中设置 java 编译器

    我正在进入 Java7 开发 并且我已将 JDK7 添加到Java 平台并在项目属性中选择它 但是当我编译时 我收到如下消息 warning java lang Boolean class java lang Boolean class m
  • 如何“对齐”2个字符串?

    首先让我解释一下 对齐 的含义 假设我们必须字符串 例如AGBCDEFABCDIEFK 他们的 结盟 是 A G B C D E F A B C D I E F K A B C D E F 另一个 相当简单的 例子 因为我相信我的问题可能与
  • 在 React 中使用 setState 更新对象

    我如何将多个状态传递给setState 这是一个例子 getInitialState function return list newFilm searchWord searchDetails componentDidMount funct
  • 使用javascript在两种颜色之间切换的最佳方法?

    Javascript初学者在这里 我本质上想做一个简单的切换 如果元素是黑色的 请将其更改为白色 如果是白色 请将其更改为黑色 function changeClass if document getElementById myButton
  • std::chrono 在当前日期上添加天数

    我想使用 std chrono 来查找根据过期时间计算未来的日期 过期期限是一个整数 指定 从现在起的天数 那么如何使用 chrono lib 来查找 100 天后的日期呢 假设你有一个time point http en cpprefer
  • `printf("...") || 的语义printf(“…”) || printf("…")`

    我想知道以下语句在 C 中会打印什么 printf hello n printf goodbye n printf world n 我通常习惯使用 cout 在 C 中打印一些内容 此外 我对这种方式使用的管道和双管道运算符感到困惑 谢谢你
  • 使用brew在MacOSx上安装Redis JSON

    如何使用brew 在 macOSx 上安装 RedisJSON 如何在不编译redis的情况下启用redis上的模块 我不想使用 docker 客户端 Redis Stack 可能是最简单的方法 它不仅仅是 RedisJSON 还包括 Re
  • 从 Node JS 中的生成器获取返回值

    我似乎无法弄清楚如何获取生成器的返回值 有人知道我做错了什么吗 function getGeneratorReturn var generator runGenerator var generatorReturn null var done
  • 使用 Mimes 验证 Laravel 文件 Post - Word 文件

    我有一张已发布的表格 我正在尝试使用验证来仅接受 Word 文档 我尝试使用哑剧类型 但似乎不起作用 而且我无法发现我的错误
  • SQLAlchemy 中的复杂外键约束

    我有两张桌子 SystemVariables and VariableOptions SystemVariables应该是不言自明的 并且VariableOptions包含所有变量的所有可能选择 VariableOptions有一个外键 v