优雅地从has_many中选择属性:通过Rails中的连接模型

时间:2009-01-09 13:39:30

标签: ruby-on-rails ruby associations has-many has-many-through

我想知道在has_many中通过连接模型选择属性的最简单/最优雅的方法是:通过关联是。

假设我们有Items,Catalogs和CatalogItems,其中包含以下Item类:

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

另外,假设CatalogueItems有一个position属性,并且任何目录和任何项目之间只有一个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.catalogs.first.position,因为我们已经完全指定了我们想要的位置:对应于@ item目录的第一个位置。

我发现得到这个的唯一方法是:

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

现在我可以做Item.catalogs.first.position。但是,这看起来有点像黑客 - 我在Catalog实例上添加了一个额外的属性。它还开辟了在两种不同情况下尝试使用视图的可能性,在这种情况下,我使用Catalog.find或@ item.catalogs填充@catalogs。在一种情况下,位置将在那里,而在另一种情况下,它不会。

有没有人有这个好的解决方案?

感谢。

4 个答案:

答案 0 :(得分: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 :(得分:0)

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

public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> e, final InternalScanner s,
        final List<Result> results, final int limit, final boolean hasMore) throws IOException {
    logger.info("result size: " + results.size());

    if (filterList != null) {
        List<Filter> li = filterList.getFilters();
        SingleColumnValueFilter sf = (SingleColumnValueFilter) li.get(0);
        byte[] family = sf.getFamily();
        byte[] qualifier = sf.getQualifier();
        byte[] startValue = sf.getComparator().getValue();
        byte[] endValue = ((SingleColumnValueFilter) li.get(1)).getComparator().getValue();
        String qualifierStr = new String(qualifier);
        String shortQualifier = qualifierStr.split("_", -1)[0].substring(0, 1).toLowerCase()
                + qualifierStr.split("_", -1)[1].substring(0, 1).toLowerCase();
        byte[] startKey = e.getEnvironment().getRegionInfo().getStartKey();
        if (startKey.length == 0) {
            logger.info("filterList first region");
            startKey = "00".getBytes();
        }
        Scan scan = new Scan();
        String scanPre = new String(startKey) + "|" + shortQualifier + "|";
        byte[] startRow = Bytes.add(scanPre.getBytes(), startValue);
        byte[] stopRow = Bytes.add(scanPre.getBytes(), endValue);
        logger.info("filterList scanPre: " + scanPre);
        logger.info("filterList startRow: " + startRow);
        logger.info("filterList stopRow: " + stopRow);
        scan.setStartRow(startRow);
        scan.setStopRow(stopRow);
        Region region = e.getEnvironment().getRegion();
        RegionScanner rs = region.getScanner(scan);
        info = "region scan";
        results.clear();
        try {
            while (true) {
                List<Cell> cells = new ArrayList<Cell>();
                rs.next(cells);
                if (cells.isEmpty()) {
                    break;
                }
                if (cells.size() > 1) {
                    logger.info("filterList finde index to row: " + cells.size());
                }
                for (Cell cell : cells) {
                    String rk = new String(CellUtil.cloneRow(cell)).split("-", -1)[1];
                    Get get = new Get(rk.getBytes());
                    results.add(region.get(get));
                }
            }
        } finally {
            rs.close();
        }
        e.bypass();
        logger.info("filterList here pass scan");
        logger.info("filterList result size: " + results.size());
        filterList = null;
        return false;
    }

    e.bypass();
    logger.info("at last result size: " + results.size());
    logger.info(hasMore ? "1" : "0");
    return hasMore;
}

答案 2 :(得分:-1)

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

class Catalog < ActiveRecord::Base
  belongs_to :catalog_item
end

现在你可以做Catalog.first.catalog_item.position。

答案 3 :(得分:-2)

你为什么不

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

为什么要浏览目录?这对我来说没有任何意义,因为你正在寻找第一个目录!?