Rails 4.0.0 を学習プロジェクトとして使用して、簡単な映画データベースを作成しようとしています。そもそも私が RoR に惹かれた機能の 1 つであるため、可能な限りスキャフォールディングを使用することに特に関心があります。
はい、私は潜在的なリスクを認識しています(Box 1.2.足場: より迅速に、より簡単に、より魅惑的に)、しかし、内部で何が起こっているかを本当に理解するまで、プロジェクトを公開しないことを約束します。現在、私は「次の超大型プロジェクトのための技術を評価している」モードになっています。
ここに私がこれまでに持っているものがあります:
rails g scaffold Person name:string
rails g scaffold Movie name:string
今、私は次のようなことをすることができました
rails g scaffold Movie name:string person_id:integer
代わりに、映画を監督と俳優の両方に関連付けたいと考えています。(次のステップは、複数の俳優を 1 つの映画に関連付ける関連付けを作成することですが、まだ十分ではありません。)
それで、私はブログ記事の作成に向かいました 同じテーブルで複数の関連付けを作成する 、必要なもののほとんどを説明しています。少し古い投稿なので、現在は変わっている可能性があります-わかりません。ともかく。これは私がモデルをどのように変更したかです:
class Person < ActiveRecord::Base
has_many :movies
end
と
class Movie < ActiveRecord::Base
belongs_to :director_id, :class_name => 'Person', :foreign_key => 'person_id'
belongs_to :actor_id, :class_name => 'Person', :foreign_key => 'actor_id'
end
そして最後はマジカル
rake db:migrate
コンソールで実行して WEBrick を起動rails s
し、ブラウザを開いてユーザーの登録を開始します。
いよいよ動画登録開始です。以前の質問hereおよびhereによると、必要なデータベース フィールドを作成するために移行スクリプトを作成する必要があります。だからこれは私がやったことです:
rails g migration AddPersonIdsToMovies director_id:integer actor_id:integer
も更新しapp/views/movies/_form.html.erb
ました
<%= form_for(@movie) do |f| %>
<% if @movie.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@movie.errors.count, "error") %> prohibited this movie from being saved:</h2>
<ul>
<% @movie.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :director_id %><br>
<%= f.select :director_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="field">
<%= f.label :actor_id %><br>
<%= f.select :actor_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
新しいムービーを作成すると、ビューが正常に表示され、選択入力が正常に機能します。ただし、ディレクターおよび俳優フィールドのデータは保持されません。rails console
新しく作成されたムービーを実行して見ました。
irb(main):004:0> mov = Movie.first
Movie Load (0.2ms) SELECT "movies".* FROM "movies" ORDER BY "movies"."id"
ASC LIMIT 1 => #<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>
監督や俳優の情報がないのはちょっと残念です。
アップデート
@Mattherick の提案に基づいて、のプライベート部分を次のように編集しましたmovies_controller.rb
。
# 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
残念ながら、新しい映画を投稿すると、
Person(#70319935588740) が期待され、String(#70319918738480) が得られました
抽出されたソース:
# POST /movies.json def create @movie = Movie.new(movie_params) respond_to do |format| if @movie.save
リクエストデータは次のようになります
{"utf8"=>"✓", "authenticity_token"=>"???", "movie"=>{"name"=>"ロマンティック コメディ", "director_id"=>"2", "actor_id"= >"1"}, "commit"=>"ムービーの作成"}
更新 2
次のように、レールコンソールで新しいムービーを作成しようとしました:
irb(main):001:0> movie = Movie.new(name: "My fine movie", director_id: "1", actor_id: "2")
ActiveRecord::AssociationTypeMismatch: Person(#70311109773080) expected, got String(#70311102311480)
これは、POST からコントローラーへの期待どおりです。director_id
これにより、との引用符を除外するとどうなるかをテストしましたactor_id
。だから私はやった
irb(main):005:0> movie = Movie.new(name: "My fine movie", director_id: 1, actor_id: 2)
ActiveRecord::AssociationTypeMismatch: Person(#70282707507880) expected, got Fixnum(#70282677499540)
引き続きコンソールを使用して、アクターとディレクターを作成することにしました
director = Person.new(name: "Woody Allen")
director.save
actor = Person.new(name: "Arnold Schwarzenegger")
actor.save
そして、私はやった
movie = Movie.new(name: "I'd like to see that", director_id: director, actor_id: actor)
movie.save
これは魅力的に機能しました(簡潔にするために出力を省略しました)。したがって、質問全体は「どのようにすればWeb インターフェースdirector_id
との間で引数として Person を渡すことができますか?」ということになります。actor_id
ムービーに という単一のフィールドがあった場合person_id: integer
、Rails は、人物の ID を含む文字列を渡そうとしているのではなく、人物オブジェクト全体を渡そうとしていると推測したと思います。
アップデート 3
外部キー列がパターン [table]_id にちなんで名付けられた場合、Rails は投稿の処理方法を理解しているのではないかという疑いをテストしました。Continent
そこで、モデルとモデルをCountry
使用して新しいプロジェクトを作成しましたrails g scaffold Country name:string continent_id:integer
。Country
含めるようにビューを変更しました
<div class="field">
<%= f.label :continent_id %><br>
<%= f.select :continent_id, Continent.all.collect {|x| [x.name, x.id]} %>
</div>
デフォルトの数値フィールドの代わりに。continent_id
まだ文字列が投稿されています:
Started POST "/countries" for 127.0.0.1 at 2013-08-03 10:40:40 +0200
Processing by CountriesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"???", "country"=>{"name"=>"Uganda", "continent_id"=>"4"}, "commit"=>"Create Country"}
それでもレールは、それがテーブルcontinent_id
内のエントリの識別子であることを理解していました。Continent
Movie
悲しいことに、テーブルに対して2 つの関連付けがあるため、元のケースでは推論メカニズムは機能しませんPerson
。director_id
からへのマッピングがあることをレールが理解していることをどうにかして確認する必要がありPerson
ます。
更新 4
一部の情報源によると、Person
モデルをさらに改良する必要があるようです。だから私はやった
class Person < ActiveRecord::Base
has_many :directed_movies, :class_name => 'Movie', :foreign_key => 'director_id'
has_many :acted_movies, :class_name => 'Movie', :foreign_key => 'actor_id'
end
しかし、まだ私の問題を解決していません。
私はちょっと迷っています。ここで欠けているものについて、誰かが私にいくつかの指針を教えてもらえますか?director_id
からへのマッピングを手伝ってくれる人はいますperson_id
か? ありがとう!