在Rails中优雅地从has_many :through关联模型中选择属性

5
我想知道在has_many :through关联中从join模型中选择属性的最简单/最优雅的方法是什么。
假设我们有Items、Catalogs和CatalogItems,以下是Item类的定义:
class Item < ActiveRecord::Base
      has_many :catalog_items
      has_many :catalogs, :through => :catalog_items
end    

此外,假设CatalogueItems具有位置属性,并且在任何目录和任何项目之间只有一个CatalogueItem。
检索位置属性最明显但略微令人沮丧的方法是:
@item         = Item.find(4)
@catalog      = @item.catalogs.first
@cat_item     = @item.catalog_items.first(:conditions => {:catalog_id => @catalog.id})
position      = @cat_item.position

这很令人困扰,因为我们已经完全指定了我们想要的位置:与@item的第一个目录对应的位置,所以我们应该能够执行@item.catalogs.first.position。

我找到的唯一方法是:

class Item < ActiveRecord::Base
      has_many :catalog_items
      has_many :catalogs, :through => :catalog_items, :select => "catalogue_items.position, catalogs.*"
end

现在我可以使用Item.catalogs.first.position。然而,这似乎有点像一个hack-我正在为Catalog实例添加一个额外的属性。这也打开了尝试在两种不同情况下使用视图(其中一种情况下,我用Catalog.find来填充@catalogs,另一种情况下,我用@item.catalogs填充)的可能性。在一种情况下,位置将在那里,在另一种情况下,位置则不存在。

有人有好的解决方案吗?

谢谢。

4个回答

0

只是添加答案,以便帮助其他人

CatalogItem.joins(:item, :catalog).
  where(items: { id: 4 }).pluck(:position).first

0
你可以像这样做:
# which is basically same as your "frustrating way" of doing it
@item.catalog_items.find_by_catalogue_id(@item.catalogs.first.id).position

或者你可以把它包装成 Item 模型的实例方法:

def position_in_first_catalogue
  self.catalog_items.find_by_catalogue_id(self.catalogs.first.id).position
end

然后只需像这样调用它:

@item.position_in_first_catalogue

1
实例方法可以工作,但似乎应该有更好的方法。我并不特别想要第一个目录,我只是用它作为一个例子。我猜你可以使用 @item.position_in_catalog(catalog_id)。 - arjun
是的,重写@otem.position_in_catalog(catalog)非常容易。然而,我没有看到其他实现你想要的方式。 - Milan Novota
@item.catalogs.first.position <-- 这并没有指定任何位置。使用 @item.catalogs 只是指定了所有目录的一个子集,然后从中选择第一个。因此,最终得到的只是一个目录,不关心你获取它的方式。 - Milan Novota
你没有注意到第二个例子中添加的:select语句。这样做会使得如果你拉取一个项目的目录,它将连同位置一起拉取。这似乎是最方便的方式,但也似乎很危险。 - arjun
1
不需要在那里调用self!catalog_items.find_by_catalogue_id就可以了。 - Ryan Bigg

-1

如果您提供关联的另一端,您应该能够执行@catalog.catalog_item.position。

class Catalog < ActiveRecord::Base
  belongs_to :catalog_item
end

现在你可以执行 Catalog.first.catalog_item.position。

一个目录拥有许多目录项(catalog_items),并且通过目录项拥有许多物品(items),所以这不完全是我要找的...谢谢。 - arjun
这种方法无法处理通过 has_many :through 表达的多对多关系。 - Andrew Champion

-2

为什么不直接

@item = Item.find(4)
position = @item.catalog_items.first.position

为什么要浏览目录?对我来说毫无意义,因为你只是在寻找第一个任何目录!?

我不确定你所说的“第一个任意目录”是什么意思。无论如何,我并不一定要寻找第一个目录 - 那只是一个例子。假设一个项目有50个目录,我想知道该项目在第6823个目录中的位置。 - arjun
我的意思是,对于当前项,似乎您只需要从catalog_items中获取第一个位置,而catalogs对于此查询是不必要的。 - aivarsak
你不知道你想要从catalog_items中获取哪个位置。如果一个物品属于50个目录,而你想要它在第47个目录中的位置(但不知道目录的顺序),那么你需要这个目录。 - arjun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接