我有3个型号,分类,产品和项目。
目标是获取所有具有产品的类别(category.products.count> 0)以及产品所属的项目未标记为私有的列表。
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :project
belongs_to :user
belongs_to :category
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :products
end
class ApplicationController < ActionController::Base
protected
def set_categories
@categories = Category.joins(:products).where("products.is_product = true or
products.is_loving_this = true")
category = Category.find_by(title: "All")
@categories.unshift(category).uniq!
end
end
views/layouts/_title_bar.html.haml
%nav.navbar-custom1.navbar.navbar-custom1.navbar-default.navbar-static-top{role: "navigation"}
.container
.col-xs-4
%ul.nav.navbar-nav
%ul.dropdown#dropdown_title_bar
%a.dropdown-toggle{"data-toggle" => "dropdown", href: "#", type: "button"}
Products
%b.caret
%ul.dropdown-menu{"aria-labelledby" => "dropdown-menu", role: "menu"}
- @categories.each do |c|
- if c.products.count > 0 || c.title == "All"
%li= link_to c.title, category_path(c)
上面的代码为我提供了所有类别的下拉列表,其中category.products.count&gt; 0但我无法弄清楚如何只获得product.project.private == false的产品。
任何人都可以指导我如何添加它吗? 我想过做2个循环,但看起来很乱。
- @categories.each do |c|
- if c.products.count > 0 || c.title == "All"
- c.products.each do |product|
- if product.project && product.project.private == false
%li= link_to c.title, category_path(c)
最后一个问题是有时产品没有项目,这就是我添加第一个if语句的原因 - 如果是product.project&amp;&amp; product.project.private == false
先谢谢你们。
答案 0 :(得分:2)
我建议你提出一些范围来帮助解决这个问题。
app/models/product.rb
# BTW, can you come up with a better name for this? When is a product not a product?
scope :is_product, -> { where(is_product: true )}
scope :is_loving_this, -> { where(is_loving_this: true) }
scope :is_public, -> { include(:project).where(projects: { private: false }) }
然后您的查询可能类似于:
app/models/category.rb
def self.active_categories
category_ids = Products.is_public.is_product.is_loving_this.map(&:category_id)
Category.find(category_ids)
end
注意,这都是未经测试的,所以请写一些测试来验证......:)
答案 1 :(得分:1)
如果你想在一个范围内做,你可以使用一个子查询(Rails在两个单独的查询中完成):
scope :wanted_categories, -> { Category.joins(:products).where("
(products.is_product = true or
products.is_loving_this = true) and products.id in (?)",
Product.joins(:projects).where("projects.private
= false").pluck(:id) }
Rails首先运行子查询并获取项目非私有的产品ID列表,然后使用该列表填充主查询的SQL集(请注意问号周围的括号)。
当您查找的值为IN
时,此方法可以顺利运行,但如果您需要将其与NOT IN
一起使用,则会有一个警告:
如果子查询没有返回任何元素,则rails将使用NULL
填充sql集,这意味着什么都不匹配,products.id in (NULL)
和products.id not in (NULL)
都不返回任何内容。在这种情况下,您可能希望将子查询转换为范围并使主范围成为条件,并使其仅在子查询返回某些内容时运行。 E.g:
class Product
scope :of_public_project, -> { joins(:projects).where("projects.private = false") }
class Category
scope :wanted_categories, -> { joins(:products).where("(products.is_product = true
or products.is_loving_this = true) and products.id not in
(?)", Product.of_public_project.pluck(:id) if Product.of_public_project.any?}
答案 2 :(得分:0)
谢谢Jason,
然而,使用你的建议,love_this永远不属于一个项目,所以我可以按照你的建议将所有方法链接在一起。
我必须将它们分开:
def get_categories
category_ids = Product.is_public.product_type.map(&:category_id)
loving_this_ids = Product.loving_this_type.map(&:category_id)
@categories = Category.find(category_ids, loving_this_ids)
end
你认为这样可以吗?或者有更好的方法吗?