What does "first" do in Ruby and Rails?

12,118

Solution 1

find_by_foo returns a single object. where returns a collection (relation). Obviously you can't do photo.photo_id when photo is a collection of Photo objects (instead of a single Photo).

first returns the first object of a collection. So by calling first on the collection returned by where, you get a single Photo object that you can work with.

Note that Foo.where(:id => bla).first is the same as Foo.find(bla), so you can just use

PhotoAlbum.find( Photo.find(params[:photo_id]).photo_album_id )

instead of

PhotoAlbum.where(:id => Photo.where(:id => params[:photo_id]).first.photo_album_id).first

Solution 2

I don't have commenting ability, so I was not able to comment on the above post, but I should point out:

Foo.where(:id => X).first is NOT the same as Foo.find(X). Foo.find(X) will raise an exception if there's no record with id = X, while the other one will just return nil.

Share:
12,118
LondonGuy
Author by

LondonGuy

Loving the experience.

Updated on June 28, 2022

Comments

  • LondonGuy
    LondonGuy almost 2 years

    I implemented a feature that lets users set default profile photos and photo-album cover photos.

    I have the following tables in my database:

    • User(s) - has one profile, has many PhotoAlbums
    • Profile(s) - belongs to user
    • PhotoAlbum(s) - belongs to user, has many photos
    • Photo(s) - belongs to PhotoAlbum

    The action for setting a default profile photo is:

    def set_default_profile_photo
      photo = Profile.find_by_user_id(current_user.id)
      photo.photo_id = params[:photo_id]
      photo.save
      redirect_to :back
      flash[:success] = "Default photo set!"
    end
    

    That works fine.

    Setting my default album photo was a little trickier:

    def set_default_album_photo
      photo = PhotoAlbum.where(:id => Photo.where(:id => params[:photo_id]).first.photo_album_id)
      photo.first.photo_id = params[:photo_id]
      photo.first.save
      redirect_to :back
      flash[:success] = "Default album photo set!"
    end
    

    This is the action that didn't work:

    def set_default_album_photo
      photo = PhotoAlbum.where(:id => Photo.where(:id => params[:photo_id]).first.photo_album_id)
      photo.photo_id = params[:photo_id]
      photo.save
      redirect_to :back
      flash[:success] = "Default album photo set!"
    end
    

    I got it working, and the undefined photo_id method error went away, but that was after changing these lines from:

    photo.photo_id = params[:photo_id]
    photo.save
    

    to:

    photo.first.photo_id = params[:photo_id]
    photo.first.save
    

    I am not sure what is happening, and why that change made things work, and want to understand what is going on.