如何在Rails 4中获取多态嵌套关系中的所有对象

时间:2014-07-20 13:30:35

标签: ruby-on-rails activerecord ruby-on-rails-4 rails-activerecord

我有以下课程

class State < ActiveRecord::Base
  has_many :cities
  has_many :products, as: :geography
end

class City < ActiveRecord::Base
  has_many :neighborhoods
  has_many :products, as: :geography
end

class Neighborhood < ActiveRecord::Base
  has_many :products, as: :geography
end

class Product < ActiveRecord::Base
  belongs_to :geography, polymorphic: true
end

class User < ActiveRecord::Base
  belongs_to :state
end

显然我有另一个class User。用户只有权查看他/她products中的state(除非用户是admin,在这种情况下,他/她可以看到所有products)我如何获得User的所有产品?

我尝试将has_many, :through添加到State,如下所示

has_many :cities_activities, through: :cities, source: :products

has_many :neighborhoods_activities, through: :neighborhoods, source: :products

def owned_activities
  self.products + self.cities_activities + self.neighborhoods_activities
end

但是owned_activities会返回Array而不是ActiveRecord::Relation(我需要某种方式返回ActiveRecord::Relation,因此我可以对其进行申请chained scopes)。

我正在用if块修补代码 - 代码变得混乱和丑陋,我怎么能以干净的轨道方式做到这一点?

@JTG建议使用merge,但显然它不能以嵌套方式工作(或者更多的是三个表/模型):

Started GET "/api/products.json?order=total_cost&page=1&per_page=18" for 127.0.0.1 at 2014-07-20 09:27:29 -0500
Processing by ProductsController#index as JSON
  Parameters: {"order"=>"total_cost", "page"=>"1", "per_page"=>"18"}
Geokit is using the domain: localhost
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
  Role Load (0.3ms)  SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 ORDER BY "roles"."id" ASC LIMIT 1  [["id", 5]]
  State Load (0.2ms)  SELECT "states".* FROM "states" WHERE "states"."id" = $1 ORDER BY "states"."id" ASC LIMIT 1  [["id", 17]]
   (9.2ms)  SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "polygons" ON "products"."geography_id" = "neighborhoods"."id
" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1  [["geography_id", 17], ["geography_type", "State"], ["states_id", 17]]
PG::DuplicateAlias: ERROR:  table name "cities" specified more than once
: SELECT COUNT(*) FROM "products" INNER JOIN "cities" ON "products"."geography_id" = "cities"."id" AND "products"."geography_type" = 'City' INNER JOIN "neighborhoods" ON "products"."geography_id" = "neighborhoods"."id" AND "products"."geography_type" = 'Neighborhood' INNER JOIN "cities" ON "neighborhoods"."cities_id" = "cities"."id" WHERE "products"."geography_id" = $1 AND "products"."geography_type" = $2 AND "cities"."states_id" = $1
ERROR:  table name "cities" specified more than once
 -- Clase: 

2 个答案:

答案 0 :(得分:1)

多态关联不适合这些关系...

You can use a nested has_many.

class State < ActiveRecord::Base
  has_many :cities
  has_many :products, through: :cities
end

class City < ActiveRecord::Base
  has_many :neighborhoods
  has_many :products, through: :neighborhoods
end

class Neighborhood < ActiveRecord::Base
  has_many :products
end

class Product < ActiveRecord::Base
  belongs_to :neighborhood
end

class User < ActiveRecord::Base
  belongs_to :state
end

然后你应该可以运行user.state.products

更好是将has_many :products, through: :state添加到user.rb,以便您可以运行user.products

答案 1 :(得分:1)

您不能使用merge将查询与AND组合在一起,您需要的是SQL OR查询。遗憾的是,任何ActiveRecord方法都不支持这种方法。您可以使用arel来创建此查询,但由于它将非常复杂并且包含大量子查询,因此我发现通过常规查询找到您需要的ID更容易,然后使用它们。

为此,您可以在State中实现类似的内容。

class State < ActiveRecord::Base
  has_many :cities
  has_many :neighborhoods, through: :cities

  has_many :products, as: :geography
  has_many :city_products, through: :cities, source: :products
  has_many :neighborhood_products, through: :neighborhoods, source: :products

  def all_products
    state_product_ids = product_ids
    city_product_ids = city_products.pluck(:id)
    neighborhood_product_ids = neighborhood_products.pluck(:id)
    all_product_ids = [state_product_ids, city_product_ids, neighborhood_product_ids].flatten.uniq

    Product.where(id: all_product_ids)
  end
end

您现在可以向用户查询所有产品,例如user.state.all_products