Rails 关联 has_many 通过禁止/归档解决方案?

Posted

技术标签:

【中文标题】Rails 关联 has_many 通过禁止/归档解决方案?【英文标题】:Rails associations has_many through ban/archive solution? 【发布时间】:2015-09-24 07:54:09 【问题描述】:

我是 Rails 新手,正在解决一个问题。我有两张桌子:

鞋子和袜子

一个 Shoe 可以有多个 Socks,但只有一个活动 Sock。其他袜子处于非活动状态。所有袜子也都是独一无二的,具有独特的图案。我想确保我没有相同图案的袜子。我想我可以通过三种方式做到这一点

1) 使用 table socks 中的附加列来表示活动 sock

class Shoe < ActiveRecord::Base
  has_many :socks
end

class Sock < ActiveRecord::Base
  belongs_to :shoe
end

class CreateGettingDressed < ActiveRecord::Migration
  def change
    create_table :shoes do |t|
      t.string :size
      t.timestamps null: false
    end

    create_table :socks do |t|
      t.belongs_to :shoe, index:true
      t.string :pattern
      t.boolean :active
      t.timestamps null: false
    end
  end
end

这看起来很简单,但是很麻烦。我会用 shoe_id 搜索袜子,然后取出 active_sock 并返回它的图案。我想我会在数组中索引 [active_sock, shoe_id] 以加快速度。

2) 使用附加表来归档非活动袜子

class Shoe < ActiveRecord::Base
  has_many :socks
  has_many :inactive_socks
end

class Sock < ActiveRecord::Base
  belongs_to :Shoe
end

class Inactive_sock < ActiveRecord::Base
  belongs_to :Shoe
end

class CreateGettingDressed < ActiveRecord::Migration
  def change
    create_table :shoes do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :socks do |t|
      t.belongs_to :shoe, index:true
      t.string :pattern
      t.timestamps null: false
    end


    create_table :inactive_socks do |t|
      t.belongs_to :shoe, index:true
      t.string :pattern
      t.timestamps null: false
    end
  end
end

这似乎也很麻烦,但是当您只是处理活动袜子时,它易于使用且快速。但是在买新袜子的时候,我必须用两张表检查图案。

3) 使用 has_many :through 关系

class Shoe < ActiveRecord::Base
  has_many :active_socks
  has_many :socks, through: active_socks 
end

class Active_Sock < ActiveRecord::Base
  belongs_to :Shoe
  belongs_to :Sock
end

class Sock < ActiveRecord::Base
  has_many :active_socks
  has_many :shoes, through: active_socks
end

class CreateGettingDressed < ActiveRecord::Migration
  def change
    create_table :shoes do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :socks do |t|
      t.string :pattern
      t.timestamps null: false
   end

    create_table :active_socks do |t|
      t.belongs_to :shoe, index: true
      t.belongs_to :sock, index: true
      t.string :pattern
      t.boolean :active
      t.timestamps null: false
    end
  end
end

这似乎是选项 2,但我觉得我正在使用 Rails 工具来减少麻烦。当我搜索模式时,我只是检查 socks 表,当我搜索一个 active_sock 时,我只是搜索 active_socks 表。

我读过类似的帖子,似乎选项 1 和 2 常用于已关闭帐户、禁止用户、禁止帖子、归档等。您需要区分仅略有不同的数据的情况。那里的选择似乎是看您需要什么,然后选择最适合您的选项 1 或 2。

我对 has_many 的理解似乎是当你有一段关系并且你需要额外的元数据时,你可以使用它。我认为这符合这种情况。

我是否正确设置了选项 1,索引 [shoe_id and active] 的数组是否可以让我更快地搜索?选项 3 是否适合使用 has_many?我对选项 3 的解释可行吗?

【问题讨论】:

【参考方案1】:

您的使用模式是什么?我猜您只是希望能够找到给定 Shoe 的活动 Sock,以及给定的 Sock 是活动的还是非活动的。要快速找到给定 Shoe 的活动 Sock,您只需要通过 belongs_to 关联为 Sock 提供其 Shoe 的外键。

class Sock < ActiveRecord::Base
  belongs_to :shoe
end

要确定 Sock 是否处于活动状态,请为其所有者的鞋子提供对其活动袜子的引用,如下所示:

class Shoe < ActiveRecord::Base
  belongs_to :sock
end

现在,您只需前往其所有者 Shoe 并检查 Shoe 的活动袜子是否是当前 Sock。例如。

def is_active
  owner_shoe.active_sock == self

基本上,将它们与外键关联起来就可以了! 附言您将 Socks 使用复数形式,但 Rails 约定是对模型名称使用单数。

编辑:您要求进行迁移,因此这是上面代码的迁移。警告:我已经很长时间没有在 Rails 中进行迁移了,所以可能会出现问题。

class CreateGettingDressed < ActiveRecord::Migration
  def change
    create_table :shoes do |t|
      t.belongs_to :active_sock, foreign_key: "sock_id"
      t.string :size
      t.timestamps null: false
    end

    create_table :socks do |t|
      t.belongs :owner_shoe, foreign_key: "shoe_id"
      t.string :pattern
    end
  end
end

【讨论】:

谢谢我修正了复数形式。我考虑过您所描述的解决方案,但我不确定如何在相互引用的表中实现外键。在我看来,我需要一个 has_many/one 等参考来描述该关联。我将重读active record associations guide,以便更好地理解它。 要实现和使用外键,我相信你只需要在类声明和迁移中使用关联,为该键创建一个整数列。稍后我将编辑我的帖子以获取示例迁移。 我找不到一个好的答案。我在它自己的question 中询问了它,以便未来的用户可以找到解决方案,并认为我也会让你知道。我也打算试试看会不会坏。

以上是关于Rails 关联 has_many 通过禁止/归档解决方案?的主要内容,如果未能解决你的问题,请参考以下文章

Rails:has_many 通过关联 - 我做对了吗?

如何通过rails中的关联定义has_many

Rails has_many,如何实现嵌套关联?

Rails 4找不到关联has_many,通过:关系错误

Rails:has_many 通过多态关联——这行得通吗?

Rails ActiveRecord:三个表 has_many 通过:关联