我正在建立一个有关友情关系的铁路网站。我知道在模型层面,它是一个自我参照联想。对于该关联,有has_and_belongs_to
等方法。
我的问题是:如何为此关系设置数据库级别约束。我想迁移将是这样的,它使用外键来保证参照完整性:
class CreateFriendships < ActiveRecord::Migration
def change
create_table :friendships do |t|
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :user, null: false, foreign_key: true
t.integer :accepted, null: false, default: 0
end
end
但是当我运行rake db:migrate
时,它有错误:
PG::DuplicateObject: ERROR: constraint "fk_friendships_user_id" for relation "friendships" already exists
事实上,我甚至不确定在这种情况下我是否有必要设置数据库约束,因为我已经看到一些人实现了友谊关系没有像这样的数据库约束:
create_table :friendships do |t|
t.integer :user_id
t.integer :friend_id
t.timestamps
end
根据Rails指南
Active Record方式声称智能属于您的模型,而不属于数据库。因此,诸如触发器或约束之类的功能(将某些智能推回到数据库中)并没有被大量使用。
我不确定在这种情况下是否大量使用数据库约束。
在这种情况下,我是否真的有必要设置数据库级别约束(使用外键)?或者我只需要在模型级别中实现约束?谢谢!
答案 0 :(得分:1)
您已两次声明user
关系:
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :user, null: false, foreign_key: true
似乎应该是这样的:
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :friend, null: false, foreign_key: true
回答您的问题:如何为此关系设置数据库级别约束? 答:就像你已经拥有的一样。
开发人员经常使用 rails方式并在模型中设置这些约束,但在数据库中设置它们是完全合理的。
编辑:
这样您就可以创建一个包含friend_id
class CreateFriendships < ActiveRecord::Migration
def change
create_table :friendships do |t|
t.belongs_to :user, null: false, foreign_key: true
t.integer :friend_id, null: false
t.integer :accepted, null: false, default: 0
end
add_foreign_key :friendships, :users, column: :friend_id
end
end
答案 1 :(得分:0)
我认为你对foreign_keys
在数据库架构中的角色感到困惑。
ActiveRecord只是SQL的“涂层”。
它能够形成允许您构建关联对象的查询等,因此您可以做的最重要的事情是正确关联这些对象。
在SQL中执行此操作的方法是使用foreign_key
,它基本上显示ActiveRecord(如果使用join
查询则使用SQL),这些数据与哪些数据相关联:
外键是关系数据库结构的标准元素,您可能知道。
您的数据结构失败的原因是您在user_id
表中复制了friendships
外键。
您需要参考以下内容: Rails: self join scheme with has_and_belongs_to_many?
这表明如果你想创建一个自引用连接表(比如你正在做),你需要使用以下内容:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :friends,
class_name: "User",
join_table: :friendships,
foreign_key: :user_id,
association_foreign_key: :friend_user_id
end
#db/migrate/______.rb
class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships, id: false do |t|
t.integer :user_id
t.integer :friend_user_id
end
add_index(:friendships, [:user_id, :friend_user_id], :unique => true)
add_index(:friendships, [:friend_user_id, :user_id], :unique => true)
end
def self.down
remove_index(:friendships, [:friend_user_id, :user_id])
remove_index(:friendships, [:user_id, :friend_user_id])
drop_table :friendships
end
end
请注意引用是针对user_id
和friend_user_id
的?
这是您需要确保has_and_belongs_to_many
能够关联同一模型的两个对象所需的两个外键。