ruby-on-rails - 在 Rails 上创建多对多记录

标签 ruby-on-rails ruby many-to-many favorites

我有一个简单的任务列表应用程序,上面有用户和列表,用户由 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

我原以为这会创建一个名为“Favorites”的表,自动链接用户和列表,但实际上它创建了一个名为“lists_users”的表。现在我不知道下一步该怎么做。我读到我需要为此连接表创建一个模型,但我不知道该怎么做。我运行什么命令? rails g model Favoritesrails g model ListsUsers ?我是否还要告知我要添加的字段,例如 rails g model Favorites user_id:integer list_id:integer ,或者是否有其他更好的方法,例如 rails g model Favorites user:references list:references ?这里的最佳实践是什么

除此之外,我还在 list#show 中添加了一个按钮 View 供用户单击以将该列表添加到他们的收藏夹,并且在路由它时遇到了一些麻烦。我所做的是创建一个这样的按钮:

<%= 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 中创建“收藏夹”数据库条目。 table 。为了说明,我将在此处粘贴我的“add_favorite”操作

def add_favorites
    user_id = current_user.id
    list_id = params[:list_id]

    #TODO: create the relation in lists_items table
end

我知道如果没有连接表的模型我无法让它工作,但即使我有那个模型,我也没有太多运气来寻找在 Controller 中做什么来创建它关系。不管怎样,我的模型如下:

class List < ApplicationRecord
    belongs_to :user
    has_many :users, through: :lists_users
end
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :lists
  has_many :lists, through: :lists_users
end

总而言之,我知道我缺少连接表的模型,我想一步一步地了解如何创建它、给它起什么名字等等,以及如何在我的 Controller 中继续我的操作以创建一个新的收藏条目

最佳答案

有两种方法可以在 Rails 中创建多对多关系。您所做的似乎将两者混为一谈,我怀疑这是问题的根源。

简而言之,这两种方法是:

1) has_many :other_models, through: :relation

2) has_and_belongs_to_many :other_models

主要区别在于“has_many through”方法期望连接表是一个单独的模型,如果需要可以独立于此关系进行处理,而“has_and_belongs_to_many”方法不要求连接表具有对应型号。在后一种情况下,您将不能独立处理连接表。 (顺便说一句,这使得连接表上的时间戳变得无用。)

您应该使用哪种方法取决于您的用例。 docs很好地总结标准:

The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database). (emphasis added)


现在回答您的问题:当您使用 create_join_table 时,您将其视为正在为 has_and_belongs_to_many 关系进行设置。 create_join_table 将创建一个名为 "#{table1}_#{table2}" 的表,其中的 ID 指向这些表。它也按字母顺序排列它们,这就是为什么你得到“lists_users”而不是“users_lists”的原因。如果您计划使用 has_and_belongs_to_many,这实际上是 Rails 连接表的标准命名约定,通常不应重命名。

如果您真的想使用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 不会设置它们。)

编辑:由于您不能直接接触 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 through,连接表应该被认为几乎是一个完全独立的模型,它恰好有两个外键并将两个其他模型以多对多关系连接在一起。在这种情况下,你会做类似的事情:

# 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”方法,因为它为您提供了更多的工作工具并且通常更灵活。

关于ruby-on-rails - 在 Rails 上创建多对多记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57015988/

相关文章:

ruby-on-rails - 如何删除 "DEPRECATION: stub! is deprecated. Use stub instead."消息?

ruby - 如何调试sidekiq前端?

ruby-on-rails - Heroku db pull 错误,它正在寻找 sqlite3 但我有 postgres,如何更改它?

ios - Swift Core Data 多对多关系处理,代码生成为手动/无

C# - NHibernate带来多对多关系的多条记录

ruby-on-rails - 我应该在 Rails 匹配 :controller(/:action(/:id)) or resources? 中使用哪些路线

mysql - 对不同的数据库和表进行一次性查询

ruby-on-rails - Capistrano 有多个 delayed_jobs 角色?

ruby-on-rails - rails 。对集合的特定属性求和

entity-framework - 使用映射表进行多对多关系映射