如何在Rails的Relationship模型中引用Person模型两条记录为外键?
解决Rails 5.1中Relationship模型关联两个Person记录的问题
嘿,我来帮你搞定这个Rails 5.1里的关联问题——这种一个表关联同一张表两条记录的场景确实容易踩坑,我给你一步步理清楚:
1. 修改迁移文件添加外键约束
你原来的迁移里,t.references默认不会自动创建指向people表的外键(它会默认去找person_a表,而这个表根本不存在),所以必须显式指定外键的目标表。修改后的CreateRelationships迁移代码如下:
class CreateRelationships < ActiveRecord::Migration[5.1] def change create_table :relationships do |t| # 显式指定外键指向people表 t.references :person_a, foreign_key: { to_table: :people } t.references :person_b, foreign_key: { to_table: :people } t.string :status t.timestamps end end end
如果已经运行过原迁移了,不用回滚,直接新建一个迁移来补加外键约束就行:
class AddForeignKeysToRelationships < ActiveRecord::Migration[5.1] def change add_foreign_key :relationships, :people, column: :person_a_id add_foreign_key :relationships, :people, column: :person_b_id end end
这样数据库层面就会强制person_a_id和person_b_id只能指向people表中存在的记录,避免无效数据。
2. 编写模型关联代码
person.rb
一个Person既可以作为关系里的person_a,也可以作为person_b,所以需要定义两个方向的关联:
class Person < ApplicationRecord # 作为person_a的所有关系 has_many :relationships_as_a, class_name: 'Relationship', foreign_key: 'person_a_id', dependent: :destroy # 作为person_b的所有关系 has_many :relationships_as_b, class_name: 'Relationship', foreign_key: 'person_b_id', dependent: :destroy # 可选:快速获取所有和当前用户有关联的人 def related_people Person.where(id: relationships_as_a.pluck(:person_b_id) + relationships_as_b.pluck(:person_a_id)) end end
class_name: 'Relationship'告诉Rails要关联的模型是Relationship(默认会找PersonA,不存在)dependent: :destroy是可选的,当Person被删除时,对应的关系也会被删除,避免数据库里出现孤儿记录related_people方法是个实用的封装,能一次性拿到所有关联的人
relationship.rb
Relationship模型需要分别关联到两个Person对象:
class Relationship < ApplicationRecord belongs_to :person_a, class_name: 'Person' belongs_to :person_b, class_name: 'Person' # 可选:验证不能创建自己和自己的关系 validate :different_people private def different_people errors.add(:base, "一个人不能和自己建立关系") if person_a_id == person_b_id end end
这里同样要指定class_name: 'Person',不然Rails会默认去找PersonA和PersonB模型,显然不存在。加的验证是个很实用的功能,防止出现无效的关系数据。
内容的提问来源于stack exchange,提问作者C. Ball
相关产品推荐
相关产品推荐

