Reputation: 320
I have the following method in an ActiveRecord, with some simplified classes/associations detailed as well:
class LineItem < ApplicationRecord
has_many :line_item_parts
def media
best = line_item_parts.detect do |part|
part.part.medias.any?
end
best_media = best && best.part.medias.first
best_media || product.medias.first
end
end
class LineItemPart < ApplicationRecord
belongs_to :line_item
belongs_to :part
end
class Part < ApplicationRecord
belongs_to :product
has_many :medias
end
class Media < ApplicationRecord
belongs_to :product
belongs_to :part, optional: true
end
When running it on the server in a binding.pry, I do the following:
> line_item.media #=> nil
> line_item.media #=> <Media: ...>
It sometimes will return nil the first time, but when I run it a second time it always returns the correct object. I see the following line in the logs for the first run:
Products::Media Exists (1.0ms) SELECT 1 AS one FROM "products_medias" WHERE "products_medias"."part_id" = $1 LIMIT $2 [["part_id", 472], ["LIMIT", 1]]
Executing the query manually returns a Media result, as expected. And in the second run, it's the same query but uses AR's CACHE
, but on the second run it returns the correct result (a non-nil Media object).
How is this possible? Is this an ActiveRecord bug? I don't even know where to start with googling something like this.
Gems:
Postgres version: psql (PostgreSQL) 10.2 (Debian 10.2-1.pgdg90+1)
Running in Docker for Mac.
Upvotes: 0
Views: 49
Reputation: 569
I don't see the error, but that looks like it will result in several queries anyway. Might I suggest an alternative?
def media
best_media = Media
.joins(parts: [line_item_parts: :line_items])
.where('line_items.id = ?', id)
.first
best_media || product.medias.first
end
If you want to stick with using the associations directly, you might try adding .to_a
before the enumerator method so you are explicitly operating on the results.
def media
best = line_item_parts.to_a.detect do |lip|
lip.part.medias.any?
end
best_media = best && best.part.medias.first
best_media || product.medias.first
end
Upvotes: 1