Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Created December 7, 2011 00:04
Show Gist options
  • Select an option

  • Save stevenharman/1440721 to your computer and use it in GitHub Desktop.

Select an option

Save stevenharman/1440721 to your computer and use it in GitHub Desktop.
Find an item by id, and eagerly load association if the association exists. Possible?
# This is a record, as in a vinyl.
class Record < ActiveRecord::Base
belongs_to :user
belongs_to :album
end
class Album < ActiveRecord::Base
has_many :records
belongs_to :artist
end
class User < ActiveRecord::Base
has_many :records
end
class Artist < ActiveRecord::Base
has_many :albums
end
# I want to query for a certain Album and also pull back any specific instances of
# that album owned by a given User. However, if the user doesn't own any instances
# of the Album, I still want the Album. Is that doable?
# Example, find Album #42 and include any records owned by user #99.
Album.include(:records).where('records.user_id' => 99).find(42)
# this causes RecordNotFound if user 99 does NOT have any records for Album 42.
@subdigital
Copy link
Copy Markdown

I believe what you want is this:

Album.joins(:records).where(...)

include does an INNER JOIN, whereas you want a LEFT JOIN.

@stevenharman
Copy link
Copy Markdown
Author

@stevenharman
Copy link
Copy Markdown
Author

Perhaps I should just bite the bullet and do it in two calls?

a = Album.find(42)
a_records = a.records.where('user_id' => 99)

@subdigital
Copy link
Copy Markdown

Yeah you're right. I had it backwards :).

I see what you're trying to do now. I think 2 queries is ok in this case. It's probably possible to do it in 1 though.

I believe find is not supposed to be mixed with the other query methods. It's single purpose, for getting 1 record.

What about this:

Album.include(:records).where('records.user_id = ? or records.user_id is null", 99).where(:id => 42).first 

definitely a shot in the dark.

@stevenharman
Copy link
Copy Markdown
Author

I was thinking the same thing, but it also results in an empty array. Also, I think I prefer using two queries, and sticking with the AR ARI, as it is more expressive and intention revealing.

Also, all of this is wrapped inside other objects - I treat the AR API as a private to the AR Models. For example

class Library

  def initialize(user, fetch_albums=Album)
    @owner = user
    @fetch_albums = fetch_albums
  end

  def albums
    fetch_albums.for_owner(owner)
  end

  def fetch_records_for_album(album)
    owner.fetch_records_for_album(album)
  end

  private
    attr_reader :owner, :fetch_albums
end

class Album < ActiveRecord::Base
  has_many :records
  belongs_to :artist

  #...

  def self.for_owner(owner)
    includes(:records).where(user_id: owner)
  end
end

class User < ActiveRecord::Base
  has_many :records

  #...

  def fetch_records_for_album(album)
    records.where(album_id: album)
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment