Home > database >  How to unscope query, but keep has_many relations
How to unscope query, but keep has_many relations

Time:10-02

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...

  • Related