4

I imagine a query like this.

Movie.find_or_create_by_title(title: 'foo').photos.find_or_create_by_name(name: 'bar')

The given query will create the Photo object but will not consider its parent Movie.

 => #<Photo id: 3, movie_id: nil …

Is there any way I can pass the movie to it?

Update: The reason I try to "save both at once" is because I have a validation in place that requires a Movie to have at least one photo. See: https://stackoverflow.com/a/12962317/471313

Community
  • 1
  • 1
Martin
  • 11,216
  • 23
  • 83
  • 140

2 Answers2

4

I'm using the updated Rails 3.2 syntax for find_or_create_by since it will be deprecated in Rails 4.0. The important thing is to have the accepts_nested_attributes_for in your Movie model like so:

class Movie < ActiveRecord::Base
  has_many :photos
  accepts_nested_attributes_for :photos
end

This allows you to specify a key in your model attributes with the form <relation-name>_attributes, in your case photo_attributes.

@movie = Movie.where(:title => 'foo').first_or_create :director => 'Steven Speilberg',
                                                      :photos_attributes => [{
                                                        :caption => "Thrilling!"
                                                      }]
@movie.save

After this, you just save the parent model, and that will automatically save the child models, again in your case photos. It is necessary to save the parent first because the child needs to know what id to put into the child record. So after @movie is saved, it will then place it's id in the movie_id field on the photos record. It can't save the child before the parent because then it doesn't know what id to use.

If you're using a Rails version before 3.2, it will look something like this:

@movie = Movie.find_or_create_by_title "W00t!", :director => 'Steven Speilberg',
                                                :photos_attributes => [{
                                                  :caption => "Thrilling!"
                                                }]
adimitri
  • 1,296
  • 9
  • 13
  • I'm getting a `ActiveRecord::UnknownAttributeError: unknown attribute: photo_attributes` with your solution. – Martin Oct 20 '12 at 04:39
  • Ok, since `Movie has_many :photos` it ended up working using plural and adding photos as an array: `photos_attributes: [ { name: 'bar' } ])` – Martin Oct 20 '12 at 04:53
  • Ah yes, sorry about that. I've updated the code above to reflect that just in case someone else comes across this question. Best of luck! – adimitri Oct 20 '12 at 05:06
0
movie = Movie.where(title: 'foo').first
if movie.nil?
  movie = Movie.new(title: 'foo')
  movie.photos.build(name: 'bar')
  movie.save
else
  movie.photos.create(name: 'bar')
end
varatis
  • 14,494
  • 23
  • 71
  • 114
  • Actually yes, I have a validation that requires at least one photo. See: http://stackoverflow.com/a/12962317/471313 – Martin Oct 20 '12 at 03:42