Elegantly selecting attributes from has_many :thro

2019-03-16 17:09发布

问题:

I'm wondering what the easiest/most elegant way of selecting attributes from join models in has_many :through associations is.

Lets say we have Items, Catalogs, and CatalogItems with the following Item class:

class Item < ActiveRecord::Base
      has_many :catalog_items
      has_many :catalogs, :through => :catalog_items
end    

Additionally, lets say that CatalogueItems has a position attribute and that there is only one CatalogueItem between any catalog and any item.

The most obvious but slightly frustrating way to retrieve the position attribute is:

@item         = Item.find(4)
@catalog      = @item.catalogs.first
@cat_item     = @item.catalog_items.first(:conditions => {:catalog_id => @catalog.id})
position      = @cat_item.position

This is annoying because it seems that we should be able to do @item.catalogs.first.position since we have completely specified which position we want: the one that corresponds to the first of @item's catalogs.

The only way I've found to get this is:

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

Now I can do Item.catalogs.first.position. However, this seems like a bit of a hack - I'm adding an extra attribute onto a Catalog instance. It also opens up the possibility of trying to use a view in two different situations where I populate @catalogs with a Catalog.find or with a @item.catalogs. In one case, the position will be there, and in the other, it won't.

Does anyone have a good solution to this?

Thanks.

回答1:

You can do something like this:

# which is basically same as your "frustrating way" of doing it
@item.catalog_items.find_by_catalogue_id(@item.catalogs.first.id).position

Or you can wrap it into in an instance method of the Item model:

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

and then just call it like this:

@item.position_in_first_catalogue


回答2:

Just adding answer so that it might help others

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


回答3:

You should be able to do @catalog.catalog_item.position if you provide the other end of the association.

class Catalog < ActiveRecord::Base
  belongs_to :catalog_item
end

Now you can do Catalog.first.catalog_item.position.



回答4:

Why don't You just

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

why do you go through catalogs? It doesn't make any sense to me since you are looking for first ANY catalog!?