SouthWolf
SouthWolf

Reputation: 11

How to create has_many association with "includes" and "where" conditions

Here is the situation: a store has many products, and can join multiple alliances. But the store owner may not want to display certain products in some alliances. For example, a store selling computers and mobile phones might want to display only phones in a "Phone Marts".

By default, all products in the store will be displayed in all alliances one shop joins. So I think building a product-alliance blacklist (I know it's a bad name...any ideas?) might be a convenient way.

The question is: how to create a "has_many" to reflect such relations like the behaviours of function shown_alliances and shown_products?

I need this because Car.includes(:shop, :alliances) is needed elsewhere, using functions make that impossible.

Product:

class Product < ActiveRecord::Base
  belongs_to :store
  has_many :alliances, -> { uniq }, through: :store
  has_many :blacklists

  def shown_alliances
   alliances.where.not(id: blacklists.pluck(:alliance_id))
  end
end

Store:

class Store < ActiveRecord::Base
  has_many :products
  has_many :alliance_store_relationships
  has_many :alliances, through: :alliance_store_relationships

  has_many :allied_products, -> { uniq }, through: :alliances, source: :products
end

Alliance:

class Alliance < ActiveRecord::Base
  has_many :alliance_store_relationships
  has_many :store, through: :alliance_store_relationships
  has_many :products, -> { uniq }, through: :companies
  has_many :blacklists

  def shown_produtcts
    @produtcts ||= produtcts.where.not(id: blacklists.pluck(:produtct_id))
  end
end

Blacklist:

class Blacklist < ActiveRecord::Base
  belongs_to :produtct
  belongs_to :alliance
  validates :produtct_id, presence: true,
                     uniqueness: { scope: :alliance_id }
end

Upvotes: 1

Views: 102

Answers (1)

Jay-Ar Polidario
Jay-Ar Polidario

Reputation: 6603

class Product < ActiveRecord::Base
  # ...
  has_many :shown_alliances, ->(product) { where.not(id: product.blacklists.pluck(:alliance_id) }, class_name: 'Alliance', through: :store, source: :alliances
  # ...
end

class Alliance < ActiveRecord::Base
  # ...
  has_many :shown_products, ->(alliance) { where.not(id: alliance.blacklists.pluck(:product_id) }, class_name: 'Product', through: :companies, source: :products
  # ...
end

You can specify a condition for associations. See docs here

Upvotes: 1

Related Questions