bpr*_*mas 3 ruby favorites many-to-many ruby-on-rails
我有一个简单的任务列表应用程序,其中包含用户和列表,用户由 Devise 管理,并且可以创建任务列表以及其他用户或他们自己创建的收藏夹列表。用户和列表之间的所有权关系很容易建立,但我在设置喜欢列表的用户的关系时遇到困难。我设想它毕竟是一种多对多关系,一个用户可以收藏许多列表,并且一个列表可以被许多用户收藏,这种关系发生在另一个已经存在的列表所有权的一对多关系之上用户让我犹豫了一下这是否是一个好的做法,但无论如何我还是继续了我的尝试。
目前我有两种模型,一种用于用户,一种用于列表,我尝试通过运行为收藏夹创建迁移rails g migration CreateJoinTableFavorites users lists,这导致了以下迁移
class CreateJoinTableFavorites < ActiveRecord::Migration[5.2]
def change
create_join_table :users, :lists do |t|
t.index [:user_id, :list_id] <-- I uncommented this line
# t.index [:list_id, :user_id]
t.timestamps <-- I added this line
end
end
end
Run Code Online (Sandbox Code Playgroud)
我认为这会创建一个名为“Favorites”的表,该表会自动链接用户和列表,但它创建了一个名为“lists_users”的表。现在我不知道下一步该做什么。我读到我需要为此连接表创建一个模型,但我不知道如何去做。我运行什么命令?rails g model Favorites?rails g model ListsUsers?我是否还告知我要添加的字段,例如rails g model Favorites user_id:integer list_id:integer,或者是否有其他更好的方法来做到这一点,例如也许rails g model Favorites user:references list:references?这里的最佳实践是什么
除此之外,我在list#show视图中添加了一个按钮,供用户单击以将该列表添加到他们的收藏夹中,但在路由它时遇到了一些问题。我所做的是创建一个像这样的按钮:
<%= button_to 'Add to favorites', add_favorites_path({list_id: @list.id}), method: :post %>
以及一条新路线:
post 'add_favorites', to: 'lists#add_favorites'
虽然我设法在该操作中访问列表 ID 和用户 ID,但现在我不知道如何继续在表中创建“最喜欢的”数据库条目lists_users。为了说明这一点,我将在此处粘贴我的“add_favorite”操作
def add_favorites
user_id = current_user.id
list_id = params[:list_id]
#TODO: create the relation in lists_items table
end
Run Code Online (Sandbox Code Playgroud)
我知道如果没有连接表的模型,我就无法让它工作,但即使我有这个模型,我也没有太多运气来寻找在控制器中做什么来创建该关系。无论如何,我的模型如下:
class List < ApplicationRecord
belongs_to :user
has_many :users, through: :lists_users
end
Run Code Online (Sandbox Code Playgroud)
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :lists
has_many :lists, through: :lists_users
end
Run Code Online (Sandbox Code Playgroud)
总而言之,我知道我缺少连接表的模型,并且希望逐步了解如何创建它、给它起什么名称等,以及如何在我的框架内继续进行在我的控制器中创建新的收藏夹条目的操作
在 Rails 中创建多对多关系有两种方法。你所做的似乎将两者混为一谈,我怀疑这是你问题的根源。
简而言之,这两种方法是:
1)has_many :other_models, through: :relation或
2)has_and_belongs_to_many :other_models
主要区别在于,“has_many through”方法期望连接表是一个单独的模型,如果需要,可以独立于这种关系进行处理,而“has_and_belongs_to_many”方法不要求连接表具有相应的模型。在后一种情况下,您将无法独立处理连接表。(顺便说一句,这使得连接表上的时间戳毫无用处。)
您应该采用哪种方法取决于您的用例。该文档很好地总结了这些标准:
最简单的经验法则是,如果您需要将关系模型作为独立实体使用,则应该设置 has_many :through 关系。如果您不需要对关系模型执行任何操作,那么设置 has_and_belongs_to_many 关系可能会更简单(尽管您需要记住在数据库中创建连接表)。(强调已添加)
现在回答您的问题:当您使用 时create_join_table,您将其视为正在为 has_and_belongs_to_many 关系进行设置。将创建一个以指向这些表的 idcreate_join_table命名的表。"#{table1}_#{table2}"它也按字母顺序排列它们,这就是为什么你得到“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
Run Code Online (Sandbox Code Playgroud)
瞧。不需要Favorite模型,并且 Rails 足够智能,可以通过表格自行处理关系。虽然更容易一些,但缺点是,如上所述,您将无法将连接表作为独立模型来处理。(同样,在这种情况下,连接表上的时间戳是无用的,因为 Rails 不会设置它们。)
编辑:由于您无法直接触摸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
Run Code Online (Sandbox Code Playgroud)
另一方面,如果您想使用“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
Run Code Online (Sandbox Code Playgroud)
您可能需要考虑使用哪种方法。同样,这实际上取决于您的用例以及上述标准。就我个人而言,当我不确定时,我更喜欢“has_many through”方法,因为它为您提供了更多可供使用的工具,并且通常更灵活。