0

I have 2 Rails models Order and CustomLogo and would like to prevent an n+1 query on some code that can be resumed to:

Order.where(some_query).each do |order|
  CustomLogo.find_by client_id: order.client_id, origin_value: order.origin
end

basically what I'd like to do is define a has_one :custom_logo on Order (that would do the proper join matching the query above) and use Order.includes(:custom_logo)

The code would then become

Order.where(some_query).includes(:custom_logo).each do |order|
  order.custom_logo
end

However I did not find a way to define the proper has_one relationship for that.

Thanks

  • 1
    What is the actual relationship between these tables in the database? You should declare it as such, then use a proper technique to avoid n+1. But you don't want to declare it a has-one if it really isn't. – lurker Jan 31 '18 at 23:23

1 Answers1

1

You can handle associations without foreign keys by redefining where in a condition proc:

class Order < ApplicationRecord
  has_one :custom_logo, 
    ->(order) { 
      unscope(:where).where(origin_value: order.origin, client_id: order.client_id) 
    }
end

Hat tip to @dwight on this answer.

Tom Copeland
  • 1,221
  • 10
  • 10
  • 1
    Thanks Tom for your suggestion. That is nice indeed. However it does not really solve the problem here as it gives a working has_one relation, which will improve the code, but the includes does not work on dynamic relations as the instance passed to the lambda is nil in such cases. My conclusion on this is that what I hoped to achieve is not supported by the framework. My current solution is to do manually a second query to retrieve all logos at once afeter retrieving orders (this is what the inludes would have don anyway). – user4887419 Feb 08 '18 at 08:26
  • Good point about the includes clause, you're right about that. A `has_one` association like this has other pitfalls as well around helper methods; `Order.first.create_custom_logo` behaves erratically and `Order.first.custom_logo = CustomLogo.last` raises an exception since it's attempting to assign a foreign key. Definitely interesting to see how it breaks down. – Tom Copeland Feb 08 '18 at 19:41