具有自身外键的 Rails 模型
我有一个 Oracle DB 模式,其中包含一个“用户”表。该表有两个非空外键,分别指向编辑者和创建者(同时也是用户)。
架构转储如下所示:
create_table "users", :force => true do |t|
t.integer "creator_id", :precision => 38, :scale => 0, :null => false
t.integer "editor_id", :precision => 38, :scale => 0, :null => false
end
add_foreign_key "users", "users", :column => "creator_id", :name => "r_user_creatorid", :dependent => :nullify
add_foreign_key "users", "users", :column => "editor_id", :name => "r_user_editorid", :dependent => :nullify
我的用户模型如下所示:
class User < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
belongs_to :editor, :class_name => "User"
validates_presence_of :creator, :editor
end
当我尝试保存第一个用户时,问题就出现了。尚不存在其他用户,但我不能有空的 editor_id 或 Creator_id。如果我尝试将编辑器和创建器设置为自身,则会出现堆栈溢出。
从理论上讲,所有用户(除了第一个用户)都有一个创建者和编辑者是有道理的。有没有办法在不暂时删除非空约束的情况下完成此任务?
I have an Oracle DB schema which includes a "users" table. This table has two non-null foreign keys to an editor and creator which are also users.
The schema dump looks like this:
create_table "users", :force => true do |t|
t.integer "creator_id", :precision => 38, :scale => 0, :null => false
t.integer "editor_id", :precision => 38, :scale => 0, :null => false
end
add_foreign_key "users", "users", :column => "creator_id", :name => "r_user_creatorid", :dependent => :nullify
add_foreign_key "users", "users", :column => "editor_id", :name => "r_user_editorid", :dependent => :nullify
My User model looks like the following:
class User < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
belongs_to :editor, :class_name => "User"
validates_presence_of :creator, :editor
end
The problem comes when I try to save the first User. No other users exist yet, but I cannot have a null editor_id or creator_id. If I try to set the editor and creator to itself, I get a stack overflow.
In theory, it makes sense that all users (except the first) have a creator and editor. Is there any way to accomplish this without temporarily removing the non-null constraint?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
所以问题是,必须有一个位于层次结构顶部的用户,该用户没有经理(在您的示例中为编辑)。这就是为什么这种结构的经典解决方案是允许空值。您在最后一段中承认了这一点:
最关键的是,如果第一个用户没有 CREATOR 或 EDITOR,那么就没有“临时”:你必须放弃强制约束。如果你这样做,问题就来了递归外键约束将消失。
另一种方法是引入亚里士多德所说的原动机,即其创建者是其本身的用户。鉴于此表:
创建这样的用户非常简单:
那么为什么这不是规范的呢?好吧,它会导致一个有点古怪的数据模型,一旦我们添加更多用户,就会对分层查询造成严重破坏。
基本上,数据库不喜欢 USERID 作为自己的编辑器。但是,有一个解决方法,那就是 <。 code>NOCYCLE 关键字(在 10g 中引入)这告诉数据库忽略层次结构中的循环引用:
这里并不重要,因为数据仍然是正确的层次结构,但是如果我们这样做会发生什么:
我们会丢失 。关系( 1 -> 7). 我们可以使用 CONNECT_BY_ISNOCYCLE 伪列来查看哪一行正在循环。
Oracle 具有许多附加功能,可以更轻松地在纯 SQL 中处理分层数据。这一切都在文档中。 了解一下更多。
So the problem is, there has to be a user at the top of the hierarchy, a user for whom there is no manager (editor in your example). That is why the classic solution to this sort of structure is to allow null values. You acknowledge this in your closing paragraph:
The kicker is, if the first user doesn't have a CREATOR or an EDITOR then there is no "temporary": you have to ditch the mandatory constraint. If you do this, the problem with the recursive foreign key constraint will disappear.
The alternative is to introduce what Aristotle called a Prime Mover, a User whose Creator is itself. Given this table:
it's pretty simple to create such a user:
So why isn't this the canonical solution. Well it leads to a slightly wacky data model which can create havoc with hierarchical queries once we add a few more users.
Basically the database doesn't like USERID being its own editor. However, there is a workaround, which is the
NOCYCLE
keyword (introduced with 10g). This tells the database to ignore circular references in the hierarchy:Here it doesn't matter because the data is still correctly hierarchical. But what happens if we do this:
We lose a relationship ( 1 -> 7). We can use the CONNECT_BY_ISNOCYCLE pseudo-column to see which row is cycling.
Oracle has lots of additional functionality to make it easier to work with hierarchical data in pure SQL. It is all in the documentation. Find out more.
我本以为你会删除 NOT NULL 约束(即允许第一个用户为创建者和编辑者设置 NULL)。
然后,您可以实施约束以确保所有后续条目不为空,例如:
I would have thought you'd remove the NOT NULL constraint (i.e. allow the first user to have NULL for creator and editor).
You can then implement constraints to make sure that all subsequent entries are not null, e.g.: