1

在我的 ROR 应用程序中,我有模型 Category、Item、Property 和 PropertyValuation。这个想法是一个类别包含项目,一个项目有几个属性。PropertyValuation 的目的是存储特定项目的属性值。模型定义如上:

class Category < ActiveRecord::Base
  attr_accessible :name, :description, :parent, :children, :items, :parent_id

  has_many :children, :class_name => "Category", :foreign_key => "parent_id", :dependent => :nullify
  belongs_to :parent, :class_name => "Category"

  has_many :categorizations
  has_many :items, :through => :categorizations
end

class Item < ActiveRecord::Base
  attr_accessible :name, :description, :property_valuations, :barcode

  has_many :property_valuations, :dependent => :destroy  
  has_many :properties, :through => :property_valuations 

  has_many :categorizations
  has_many :categories, :through => :categorizations

end

class Property < ActiveRecord::Base
  attr_accessible :name, :description, :value_type, :unit, :unit_id

  has_many :property_valuations, :dependent => :destroy  
  has_many :items, :through => :property_valuations
  has_many :property_ranges, :dependent => :destroy

  belongs_to :unit
end

class PropertyValuation < ActiveRecord::Base     
  attr_accessible :property, :item, :value, :categorization

  belongs_to :property
  belongs_to :item
end

现在我的问题是,我已经成功地通过这样做来按名称过滤类别项目:

@category.items.where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")

但现在我还想根据关联的属性值过滤这些项目。示例:我想要名称包含“foo”的类别项目,其中属性“A”的值为 1,属性 B 的值为 2,依此类推。如何实现这样的查询?

4

3 回答 3

2

您应该将表格连接在一起,然后根据您的标准进行限制。

# Category items by name
Category.joins(:items).where(:items => { :name => keywords })

您可能会发现http://guides.rubyonrails.org/active_record_querying.html#joining-tables并致电.to_sql很有帮助。

于 2013-04-08T14:25:17.120 回答
0

您可以链接 ActiveRecord 范围,包括where. 因此,您可以先限制名称,然后链接附加where以限制结果。如您的问题中所述,下面的此示例将限制属性“A”值为 1 的结果:

keywords = params[:keywords].downcase
@category.items.where("lower(items.name) like ?", "%#{keywords}%").where(:A => 1)

您还可以将范围存储在变量中。例如,如果您想分别通过属性 A 和 B 限制相同的数据集,您可以执行以下操作:

keywords = params[:keywords].downcase
matched_by_name = @category.items.where("lower(items.name) like ?", "%#{keywords}%")
foo = matches_by_name.where(:A => 1)
bar = matches_by_name.where(:B => 1)
于 2013-04-07T16:41:25.113 回答
0

可以做到这一点的宝石:activerecord_where_assoc(我是作者)

有了它,你可以用这种方式做你想做的事:
我想要名称包含“foo”的类别项目,其中属性“A”的值为 1,属性 B 的值为 2,依此类推。

@category.items.where(name: "foo").where_assoc_exists(:property_valuations) { |pv|
  pv.where(value: 1).where_assoc_exists(:property, name: 'A')
}.where_assoc_exists(:property_valuations) { |pv|
  pv.where(value: 2).where_assoc_exists(:property, name: 'B')
}

这显示了宝石的最大力量,它只是可以重复使用而不会发生冲突。用连接来做这件事会很痛苦。但是,当需求复杂时,结果也很密集......这可以通过范围轻松解决。

# In items:
scope :with_property, lambda {|name, value| 
  where_assoc_exists(:property_valuations) { |pv|
    pv.where(value: value).where_assoc_exists(:property, name: name)
}}

@category.items.where(name: "foo").with_property('A', 1).with_property('B', 2)

如果您想制作一个可以接收操作员的更强大的示波器,您可以通过更改where(value: value)部件来满足您的需要。

下面是介绍例子。阅读文档中的更多详细信息。

于 2020-02-25T13:22:53.653 回答