呃,感觉标题有点不够精确,不过暂时没想到合适的词语去描述,先将就着。
场景:
现有一个model:group群组,一个model: user用户,每一个群组被一个user创建,每一个user可以创建多个group,很明显,两者之间是一对多关系。现在每个group可以添加一个管理员,管理员与group也是一对多的关系,而群组group的管理员和创建人都来自user这个model。
也就是说,user这个model需要两次被引用,group与user之间建立了两个一对多的关联。
解决方法
分两种情况:
- 如果是新建一个项目,不涉及到数据迁移部分,可以参考Referencing the same model twice in Rails? 完成。
- model已经建立,group与user之间建立了一对多的关系,同时group有一个外键是user_id, 这个外键的约束来自user。
针对后者说一下具体的解决方法。
分两步:
给group添加两个字段creator_id,admin_id,作为reference,同时与user建立一对多的关联
迁移数据,将user_id的数据迁移给到creator_id,同时删除user_id
好,一步步来。
目前的group表结构如下:
groups:
string "title"
text "description"
datetime "created_at", null: false
datetime "updated_at", null: false
bigint "user_id"
index ["user_id"], name: "index_groups_on_user_id"
用户部分使用devise生成了user model。
第一步:给group添加字段,重新生成一对多关联
终端执行:
rails g migration add_creator_and_admin_to_group
在新生成的migration表中,添加如下内容:
class AddCreatorAndAdminToGroup < ActiveRecord::Migration[5.0]
def change
add_reference :groups, :creator
add_reference :groups, :admin
add_foreign_key :groups, :users, column: :creator_id, primary_key: :id
add_foreign_key :groups, :users, column: :admin_id, primary_key: :id
end
end
终端执行:
rake db:mgirate
这里为group添加了两个reference, creator_id, admin_id,同时通过add_foreign_key,将creator_id,admin_id指定成外键。
在app/models/group.rb
中,添加:
class Group < ApplicationRecord
......
belongs_to :creator, class_name: 'User'
belongs_to :admin, class_name: 'User', optional: true
end
这里对于admin添加了optional: true
,这样创建group的时候,不必指定admin。
在app/models/user.rb
中,添加:
class User < ApplicationRecord
......
has_many :created_groups, class_name: 'Group', foreign_key: 'creator_id'
has_many :admined_groups, class_name: 'Group', foreign_key: 'admin_id'
end
此时rails c
进入rails控制台, 可以检查下刚刚创建的关系是否成功。
User.create(email: "[email protected]", password: "123456", password_confirmation: "123456")
=> #<User id: 2, email: "[email protected]", created_at: "2018-06-09 05:43:58", updated_at: "2018-06-09 05:43:58">
User.create(email: "[email protected]", password: "123456", password_confirmation: "123456")
=> #<User id: 3, email: "[email protected]", created_at: "2018-06-09 05:44:17", updated_at: "2018-06-09 05:44:17">
a = Group.new(title: "rubyist", description: "for search")
=> #<Group:0x007ff3d0a04030
id: nil,
title: "rubyist",
description: "for search",
created_at: nil,
updated_at: nil,
user_id: nil,
creator_id: nil,
admin_id: nil>
a.user = User.last
=> #<User id: 3, email: "[email protected]", created_at: "2018-06-09 05:44:17", updated_at: "2018-06-09 05:44:17">
a.creator = User.last
=> #<User id: 3, email: "[email protected]", created_at: "2018-06-09 05:44:17", updated_at: "2018-06-09 05:44:17">
a.admin = User.find(2)
=> #<User id: 2, email: "[email protected]", created_at: "2018-06-09 05:43:58", updated_at: "2018-06-09 05:43:58">
a.save
=> true
User.last.created_groups
=> [#<Group:0x007ff3ce0a3ee8
id: 5,
title: "rubyist",
description: "for search",
created_at: Sat, 09 Jun 2018 05:47:49 UTC +00:00,
updated_at: Sat, 09 Jun 2018 05:47:49 UTC +00:00,
user_id: 3,
creator_id: 3,
admin_id: 2>]
User.second.admined_groups
=> [#<Group:0x007ff3cd61ff80
id: 5,
title: "rubyist",
description: "for search",
created_at: Sat, 09 Jun 2018 05:47:49 UTC +00:00,
updated_at: Sat, 09 Jun 2018 05:47:49 UTC +00:00,
user_id: 3,
creator_id: 3,
admin_id: 2>]
OK! 基本功能实现。下面我们将user_id的数据赋给creator_id,同时删除掉user_id这个外键。
第二步:数据迁移
没有迁移前,进rails c
, 查看Group的第一个record:
Group.first
> #<Group:0x007ff3d093d9d0
id: 3,
title: "group1",
description: "groups",
created_at: Sat, 09 Jun 2018 05:46:29 UTC +00:00,
updated_at: Sat, 09 Jun 2018 05:46:29 UTC +00:00,
user_id: 1,
creator_id: nil,
admin_id: nil>
可以看到creator_id此时为nil, user_id 为1。我们需要把user_id给到creator_id。
终端执行:
rails g migration remove_user_id_on_group
在新生成的migration表中,添加如下内容:
class RemoveUserIdOnGroup < ActiveRecord::Migration[5.0]
class Group < ActiveRecord::Base
end
def change
Group.where.not(user_id: nil).find_each do |g|
g.update(creator_id: g.user_id)
end
remove_foreign_key :groups, column: :user_id
end
end
终端执行:
rake db:mgirate
这样数据就迁移过来了,同时group的外键user_id也被删除了。
进rails console
看看:
Group.first
> #<Group:0x007ff3d093d9d0
id: 3,
title: "group1",
description: "groups",
created_at: Sat, 09 Jun 2018 05:46:29 UTC +00:00,
updated_at: Sat, 09 Jun 2018 06:05:24 UTC +00:00,
creator_id: 1,
admin_id: nil>
可以看到数据已经成功迁移了。
但是,还没有完,需要清理下代码。
在app/models/group.rb
中,删除belongs_to :user
在app/models/user.rb
中,删除has_many :groups
在app/controllers/groups_controller.rb
与group对应的views中,将user更换成creator。
OK! 完成 :)