Note that there are some explanatory texts on larger screens.

plurals
  1. PORails 4 - trouble with two associations, same model
    primarykey
    data
    text
    <p>I'm trying to make a simple movie database using Rails 4.0.0 as a learning project. I'm particularly interested in using scaffolding as much as possible, as this is one of the features that drew me to RoR in the first place.</p> <p>Yes, I do realize the <a href="http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#cha-beginning" rel="nofollow noreferrer">potential risks</a> (Box 1.2.Scaffolding: Quicker, easier, more seductive), but I promise I won't have my project go public before I <em>really</em> understand what's going on beneath the hood. Right now I'm more in "evaluating technologies for my next super duper project"-mode.</p> <p>Here's what I've got so far:</p> <pre><code>rails g scaffold Person name:string rails g scaffold Movie name:string </code></pre> <p>Now, I could've done something like</p> <pre><code>rails g scaffold Movie name:string person_id:integer </code></pre> <p>instead, but I want a movie to be associated with both a director and an actor. (Next step is to make an association that relates multiple actors to a single movie, but I'm not quite there yet.)</p> <p>So, I headed over to the blog post <a href="http://www.spacevatican.org/2008/5/6/creating-multiple-associations-with-the-same-table/" rel="nofollow noreferrer">Creating Multiple Associations With the Same Table</a>, describing pretty much what I need. It's a somewhat old post, so things might have changed now - I don't know. Anyway. This how I changed the models:</p> <pre><code>class Person &lt; ActiveRecord::Base has_many :movies end </code></pre> <p>and</p> <pre><code>class Movie &lt; ActiveRecord::Base belongs_to :director_id, :class_name =&gt; 'Person', :foreign_key =&gt; 'person_id' belongs_to :actor_id, :class_name =&gt; 'Person', :foreign_key =&gt; 'actor_id' end </code></pre> <p>and finally the magical</p> <pre><code>rake db:migrate </code></pre> <p>Starting the WEBrick by running <code>rails s</code> in the console, I open my browser and start registering people</p> <p><img src="https://i.stack.imgur.com/VF2Os.png" alt="enter image description here"></p> <p>The time has come to start registering movies. According to previous questions <a href="https://stackoverflow.com/questions/8420769/rails-how-to-migrate-a-database-where-i-added-a-belongs-to-relationship">here</a> and <a href="https://stackoverflow.com/questions/4619651/how-to-reflect-in-the-database-a-new-belongs-to-and-has-many-relationship-in-rub">here</a> I have to make a migration script in order to create the necessary database fields. So this is what I did:</p> <pre><code>rails g migration AddPersonIdsToMovies director_id:integer actor_id:integer </code></pre> <p>I also updated the <code>app/views/movies/_form.html.erb</code> to </p> <pre><code>&lt;%= form_for(@movie) do |f| %&gt; &lt;% if @movie.errors.any? %&gt; &lt;div id="error_explanation"&gt; &lt;h2&gt;&lt;%= pluralize(@movie.errors.count, "error") %&gt; prohibited this movie from being saved:&lt;/h2&gt; &lt;ul&gt; &lt;% @movie.errors.full_messages.each do |msg| %&gt; &lt;li&gt;&lt;%= msg %&gt;&lt;/li&gt; &lt;% end %&gt; &lt;/ul&gt; &lt;/div&gt; &lt;% end %&gt; &lt;div class="field"&gt; &lt;%= f.label :name %&gt;&lt;br&gt; &lt;%= f.text_field :name %&gt; &lt;/div&gt; &lt;div class="field"&gt; &lt;%= f.label :director_id %&gt;&lt;br&gt; &lt;%= f.select :director_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple =&gt; false %&gt; &lt;/div&gt; &lt;div class="field"&gt; &lt;%= f.label :actor_id %&gt;&lt;br&gt; &lt;%= f.select :actor_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple =&gt; false %&gt; &lt;/div&gt; &lt;div class="actions"&gt; &lt;%= f.submit %&gt; &lt;/div&gt; &lt;% end %&gt; </code></pre> <p>When I create a new movie, the view shows up fine and the select inputs works fine. However, the data in the director and actor field isn't persisted. I ran <code>rails console</code> and looked at the newly created movie:</p> <pre><code>irb(main):004:0&gt; mov = Movie.first Movie Load (0.2ms) SELECT "movies".* FROM "movies" ORDER BY "movies"."id" ASC LIMIT 1 =&gt; #&lt;Movie id: 1, name: "No such movie", created_at: "2013-08-02 17:02:12", updated_at: "2013-08-02 17:02:12", director_id: nil, actor_id: nil&gt; </code></pre> <p>which is kind'a disappointing with no director or actor info.</p> <p><strong>Update</strong></p> <p>Based on @Mattherick's suggesition, I edited the private part of the <code>movies_controller.rb</code> to this:</p> <pre><code># Never trust parameters from the scary internet, only allow the white list through. def movie_params params.require(:movie).permit(:name, :director_id, :actor_id) end </code></pre> <p>Unfortunately, when I post a new movie I get</p> <blockquote> <p>Person(#70319935588740) expected, got String(#70319918738480)</p> <p>Extracted source: </p> <pre><code># POST /movies.json def create @movie = Movie.new(movie_params) respond_to do |format| if @movie.save </code></pre> </blockquote> <p>and the request data goes as </p> <p>{"utf8"=>"✓", "authenticity_token"=>"???", "movie"=>{"name"=>"Romantic Comedy", "director_id"=>"2", "actor_id"=>"1"}, "commit"=>"Create Movie"}</p> <p><strong>Update 2</strong></p> <p>I tried to create a new Movie in the rails console, like this:</p> <pre><code>irb(main):001:0&gt; movie = Movie.new(name: "My fine movie", director_id: "1", actor_id: "2") ActiveRecord::AssociationTypeMismatch: Person(#70311109773080) expected, got String(#70311102311480) </code></pre> <p>which is what you'd expect from the POST to the controller. This made me test what happened if I excluded the quotation marks for <code>director_id</code> and <code>actor_id</code>. So I did</p> <pre><code>irb(main):005:0&gt; movie = Movie.new(name: "My fine movie", director_id: 1, actor_id: 2) ActiveRecord::AssociationTypeMismatch: Person(#70282707507880) expected, got Fixnum(#70282677499540) </code></pre> <p>Still using the console, I decided to create an actor and a director</p> <pre><code>director = Person.new(name: "Woody Allen") director.save actor = Person.new(name: "Arnold Schwarzenegger") actor.save </code></pre> <p>and then I did</p> <pre><code>movie = Movie.new(name: "I'd like to see that", director_id: director, actor_id: actor) movie.save </code></pre> <p>which worked like a charm (output omitted for brevity). So the whole question boils down to <strong>"How can I pass a Person as the argument to <code>director_id</code> and <code>actor_id</code> through the web interface?"</strong> </p> <p>If I had a single field in Movies called <code>person_id: integer</code>, I believe that rails would've inferred that I'm not trying to pass a string containing the id of a person, but rather I'm trying to pass an entire person object.</p> <p><strong>Update 3</strong></p> <p>I tested my suspicion that rails understands how to deal with posts when the foreign key column is named after the pattern [table]_id. So I created a new project with a <code>Continent</code> model and a <code>Country</code> model, where <code>rails g scaffold Country name:string continent_id:integer</code>. I changed my <code>Country</code> view to include</p> <pre><code>&lt;div class="field"&gt; &lt;%= f.label :continent_id %&gt;&lt;br&gt; &lt;%= f.select :continent_id, Continent.all.collect {|x| [x.name, x.id]} %&gt; &lt;/div&gt; </code></pre> <p>instead of the default numeric field. The <code>continent_id</code> is still posted a string:</p> <pre><code>Started POST "/countries" for 127.0.0.1 at 2013-08-03 10:40:40 +0200 Processing by CountriesController#create as HTML Parameters: {"utf8"=&gt;"✓", "authenticity_token"=&gt;"???", "country"=&gt;{"name"=&gt;"Uganda", "continent_id"=&gt;"4"}, "commit"=&gt;"Create Country"} </code></pre> <p>yet rails understood that <code>continent_id</code> was the identifier of an entry in the <code>Continent</code> table.</p> <p>Sadly, the inferrer mechanism doesn't work in my original case, as I have two associations from <code>Movie</code> to the <code>Person</code> table. I need to somehow make sure rails understand that there is a mapping from <code>director_id</code> to a <code>Person</code>.</p> <p><strong>Update 4</strong></p> <p>According to some sources, it seems as I need to refine the <code>Person</code> model further. So I did</p> <pre><code>class Person &lt; ActiveRecord::Base has_many :directed_movies, :class_name =&gt; 'Movie', :foreign_key =&gt; 'director_id' has_many :acted_movies, :class_name =&gt; 'Movie', :foreign_key =&gt; 'actor_id' end </code></pre> <p>but still not fixing my problems.</p> <p>I'm kind'a lost. <del>Can anyone give me a few pointers on what I'm missing here?</del> Can anyone help me map from <code>director_id</code> to <code>person_id</code>? Thanks!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload