How can I "unscope" queries but keeping has_many
relations?
I have two models. Each of them has default_scopes
, and I query with different scopes
too (visibility
, active
, etc.. )
class Shop
has_many :products
scope :active, -> { where(active: true, visible: true) }
end
class Product
belongs_to :shop
default_scope { where(:visible => true, active: true) }
end
active_shop = Shop.create(name: "active",
active: true,
visible: true)
inactive_shop = Shop.create(name: "inactive",
active: false,
visible: false)
active_shop.products.create!(name "active product",
visible: true,
active: true)
inactive_shop.products.create!(name "inactive product",
visible: false,
active: false)
Sometimes I need to change values for all related products of a specific shop.
I have to "unscope" the products to overcome the default_scope
:
But I have then the problem, that this also removes the has_many
query:
inactive_shop.products.unscoped.map(&:name)
# ["inactive product","active product"]
But I expect only to return the "inactive product"
Hint: This is a simplified example. So a simple
scope :visible, -> { where(visible: true) }
scope :active, -> { where(active: true) }
is not possible, because the scopes are more complex in my real code.
What I need is a something like this: Shop.products.all_products_of_this_shop_equally_which_scope
With unscoped
I also get ALL Products of the whole App. Unscope
also adds the Products
of the other Shop
:
>> Shop.last.products.to_sql
Shop Load (1.9ms)
SELECT "shops".* FROM "shops"
ORDER BY "shops"."id"
DESC LIMIT $1 [["LIMIT", 1]]
=> "SELECT \"products\".* FROM \"products\"
WHERE \"products\".\"visible\" = TRUE
AND \"products\".\"active\" = TRUE
AND \"products\".\"shop_id\" = 234"
>> Shop.last.products.unscoped.to_sql
Shop Load (2.7ms)
SELECT "shops".* FROM "shops"
ORDER BY "shops"."id"
DESC LIMIT $1 [["LIMIT", 1]]
=> "SELECT \"products\".* FROM \"products\""
# BUT I NEED THIS:
"SELECT \"products\".* FROM \"products\"
WHERE \"products\".\"shop_id\" = 234"
In the Product query I need to keep the relation to the shop, but wipe out all other queries
Thank you very much in advance
CodePudding user response:
I think you can try using unscope
instead of unscoped
. It allows specifying particular methods that need to be unscoped (ref), like this:
inactive_shop.products.unscope(where: %i[active visible]).map(&:name)
Or if there are many WHERE conditions in the default scope and you want to unscope all WHERE conditions except shop_id you can try something like this:
inactive_shop.products.unscope(
where: inactive_shop.products.where_values_hash.except('shop_id').keys
).map(&:name)
At this point however I would instead think of getting rid of the default scope...