在 Rails 中创建多对多关系有两种方法。你所做的似乎将两者混为一谈,我怀疑这是你问题的根源。
简而言之,这两种方法是:
1) has_many :other_models, through: :relation
or
2) has_and_belongs_to_many :other_models
主要区别在于,“has_many through”方法期望连接表是一个单独的模型,如果需要,可以独立于这种关系进行处理,而“has_and_belongs_to_many”方法不要求连接表具有相应的模型。在后一种情况下,您将not能够独立处理连接表。 (顺便说一句,这使得连接表上的时间戳毫无用处。)
您应该采用哪种方法取决于您的用例。这docs https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many很好地总结了标准:
最简单的经验法则是您应该设置 has_many :through 关系如果您需要将关系模型作为独立实体使用。如果您不需要对关系模型执行任何操作,那么设置 has_and_belongs_to_many 关系可能会更简单(尽管您需要记住在数据库中创建连接表)。(强调已添加)
现在回答你的问题:当你使用create_join_table
,您将其视为正在为 has_and_belongs_to_many 关系进行设置。create_join_table
将创建一个名为"#{table1}_#{table2}"
id 指向这些表。它也按字母顺序排列它们,这就是为什么你得到“lists_users”而不是“user_lists”。如果您打算使用,这实际上是 Rails 连接表的标准命名约定has_and_belongs_to_many
,并且一般不应重命名。
如果你真的想使用has_and_belongs_to_many
,保持迁移create_join_table
只需在模型中执行以下操作:
# user.rb
class User
has_and_belongs_to_many :lists
end
# list.rb
class List
has_and_belongs_to_many :users
end
瞧。不Favorite
需要模型,并且 Rails 足够智能,可以通过表格自行处理关系。虽然更容易一些,但缺点是,如上所述,您将无法将连接表作为独立模型来处理。 (同样,在这种情况下,连接表上的时间戳是无用的,因为 Rails 不会设置它们。)
Edit:由于您无法直接触摸lists_users,因此您可以通过在用户上设置列表关系或通过在列表上设置用户关系来创建关系,如下所示:
def add_favorites
list = List.find(params[:list_id])
current_user.lists << list # creates the corresponding entry in lists_users
# Don't forget to test how this works when the current_user has already favorited a list!
# If you want to prevent that from happening, try
# current_user.lists << list unless current_user.lists.include?(list)
# Alternatively you can do the assignment in reverse:
# list.users << current_user
# Again, because the join table is not an independent model, Rails won't be able to do much to other columns on lists_users out of the box.
# This includes timestamps
end
另一方面,如果您想使用“has_many through”,请不要使用create_join_table
。如果您使用 has_many ,则连接表应该被视为几乎完全独立的模型,它恰好有两个外键并将其他两个模型以多对多关系连接在一起。在这种情况下,你会这样做:
# migration
class CreateFavorites < ActiveRecord::Migration[5.2]
def change
create_table :favorites do |t|
t.references :list
t.references :user
t.timestamps
end
end
end
# user.rb
class User
has_many :favorites
has_many :lists, through: :favorites
end
# list.rb
class List
has_many :favorites
has_many :users, through: :favorites
end
# favorite.rb
class Favorite
belongs_to :list
belongs_to :user
end
# controller
def add_favorites
# You actually have a Favorite model in this case, while you don't in the other. The Favorite model can be more or less independent of the List and User, and can be given other attributes like timestamps.
# It's the rails methods like `save`, `create`, and `update` that set timestamps, so this will track those for you as any other model.
Favorite.create(list_id: params[:list_id], user: current_user)
end
您可能需要考虑使用哪种方法。同样,这实际上取决于您的用例以及上述标准。就我个人而言,当我不确定时,我更喜欢“has_many through”方法,因为它为您提供了更多可供使用的工具,并且通常更灵活。