在 Rails 上创建多对多记录

2024-01-10

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

我知道如果没有连接表的模型,我就无法让它工作,但即使我有这个模型,我也没有太多运气来寻找在控制器中做什么来创建该关系。无论如何,我的模型如下:

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

总而言之,我知道我缺少连接表的模型,并且希望逐步了解如何创建它、给它起什么名称等,以及如何在我的框架内继续进行在我的控制器中执行操作以创建新的收藏夹条目


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Rails 上创建多对多记录 的相关文章

随机推荐

  • 创建配置文件(config.php)php的最佳方法[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我正在为我的项目创建一个数据库配置文件 但我不确定我的 config php 是否安全 我如何修改此脚本以实现安全连接 配置文件 索引 php
  • iOS TableView 重新加载并滚动到顶部

    第二天我无法解决表的问题 我们有一个segmentedControl 当它改变时 它也会改变表 假设控件的段中有 3 个元素 相应地有 3 个数组 这一点很重要 它们的大小不同 当segmentedControl 更改时 我需要向上滚动表格
  • 根据语言选择对齐文本视图(左-右)

    我有一个线性布局 其中包含水平排列的文本视图和编辑文本 我有一个选项可以在先前的活动中选择语言 英语和阿拉伯语 当我选择英语时 当前对齐方式很好 但是当我选择阿拉伯语时 它应该从右到左显示 这意味着 textView 位置应该向右 在布局中
  • 验证来自客户的应用内购买收据

    我阅读了 几乎 所有有关验证应用内购买的答案 实际上我已经以服务器端的方式实现了它 但管理服务器有时可能会太昂贵 理论上你可以从你的应用程序进行验证 基本上只是向 Apple 发送一个 json 并获取答案 当然 我知道在越狱设备上收据可能
  • 如何将 SQL 与 Node.JS 结合使用

    所以 我对整个后端世界相当陌生 我现在正在使用 Node js 进行编码 我有一个项目 我必须存储具有几个字段的用户 但我不知道如何用节点连接数据库并进行查询 而且 我有 WAMP 堆栈 因为我曾经编写 PHP 代码 很少 我也可以将其 M
  • 如何在 swift 中从外部框架呈现视图控制器?

    我制作了一个带有名为 AuthenticationViewController h 的视图控制器和笔尖 AuthenticationViewController xib 的框架 用于测试的示例项目用于展示 AuthenticationVie
  • shared_ptr 释放[重复]

    这个问题在这里已经有答案了 可能的重复 如何从 boost shared ptr 释放指针 https stackoverflow com questions 1525764 how to release pointer from boos
  • 从父组件更改反应钩子状态

    我有一个像这样的钩子组件 import React useState from react const MyComponent props gt const value setValue useState 0 const cleanValu
  • 执行 ImageEnhance.Sharpness() 时出现“无法过滤调色板图像”错误

    我有一个 GIF 图像文件 我使用打开它PIL Image http effbot org imagingbook image htm并对其进行了一些尺寸变换 然后我尝试使用ImageSharpness Enhance http effbo
  • 在没有库的 JavaScript 中检查 IE 是否小于 9 的最佳方法

    在不使用 jQuery 或任何附加库的情况下 检测 IE 浏览器和 JavaScript 版本低于 9 的浏览器的最快 最短 最佳 方法是什么 JavaScript var ie function var undef v 3 div doc
  • 这段代码不是将值插入 SQLite 数据库吗?

    我正在为 Android 编写一个应用程序 我需要读取 txt 文件并将数据写入 SQLite 文件中 我已设法完成所有代码 但应将值插入数据库的部分无法正常工作 我给出了下面的代码 try ContentValues values new
  • 传单 - 标记未显示

    我是传单新手 在美国地图上放置标记时遇到问题 下面是我的 JSON 文件的片段 site name Chemical Minerals Reclamation city Cleveland epa id OHD980614549 npl D
  • 如何在 Mac OSX 中获取真实的日历微秒时间(自 1970 年以来的纪元)?

    请通过下面的 Qn 了解上下文 为什么 clang g 在 Mac OSX 中没有为 chrono high resolution clock now 提供正确的微秒输出 https stackoverflow com q 45882606
  • Python 2.7.10 错误“from urllib.request import urlopen”没有名为 request 的模块

    我打开了Python代码github 我以为是python2 x当我尝试运行它时出现上述错误 从阅读中我发现Python 3已经贬值了urllib本身并用许多库替换它 包括urllib request 看起来代码是用 python 3 编写
  • Node Js Index.html 加载但链接和脚本 404

    我有一个运行的 NodeJS 服务器 客户端封装在客户端文件夹中 以便轻松管理 index html 的文件夹结构 然后链接和脚本加载应该没有问题 client folder index html style css script js c
  • 当用户禁用位置访问时显示弹出窗口(Android Google 地图)

    我如何显示弹出窗口以启用您的位置访问并打开位置访问意图 现在我正在创建包含地图的应用程序 我已启用 map setMyLocationEnabled true 并且应用程序显示当前位置按钮 但当禁用位置访问时 它不会显示任何响应 我希望如果
  • jSoup - 删除特定的样式属性

    我错过了什么吗 有一个更好的方法吗 INPUT span Dr Who is u usually u available for consultations Mon Thurs afternoons and Friday 9a span
  • 在开发环境中使用 Facebook 身份验证

    当使用 OAuth2 在客户端服务器端模式下使用 facebook 身份验证时 是否有任何方法可以在使用重定向 uri 的本地开发环境时对其进行测试 显然 您可以指定您的 IP 并将该 IP 上的端口 80 路由到您的计算机 但是是否有更简
  • 执行 S3 标准存储类别的策略

    有没有办法定义 S3 存储桶策略来强制实施标准存储类别 我想阻止用户创建具有减少的冗余存储类别的对象 您现在可以在 S3 存储桶策略中使用条件来限制 S3 对象的创建 使用PutObject 到特定的存储类别 当前版本的 AWS 文档有一个
  • 在 Rails 上创建多对多记录

    我有一个简单的任务列表应用程序 其中包含用户和列表 用户由 Devise 管理 并且可以创建任务列表以及其他用户或他们自己创建的收藏夹列表 用户和列表之间的所有权关系很容易建立 但我在设置喜欢列表的用户的关系时遇到困难 我设想它毕竟是一种多