我想知道在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。在一种情况下,位置将在那里,而在另一种情况下,它不会。
有没有人有这个好的解决方案?
感谢。
答案 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
为什么要浏览目录?这对我来说没有任何意义,因为你正在寻找第一个目录!?