Rails关系问题(类别,产品和项目)

时间:2014-09-01 17:43:19

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

我有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

先谢谢你们。

3 个答案:

答案 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
你认为这样可以吗?或者有更好的方法吗?