1

我正在尝试使用 Rails 4.0.0 作为学习项目制作一个简单的电影数据库。我对尽可能多地使用脚手架特别感兴趣,因为这是最初吸引我使用 RoR 的功能之一。

是的,我确实意识到潜在的风险(方框 1.2.Scaffolding:更快、更容易、更诱人),但我保证在我真正了解幕后发生的事情之前,我不会让我的项目公开。现在我更多的是在“为我的下一个超级骗子项目评估技术”模式。

这是我到目前为止所得到的:

rails g scaffold Person name:string
rails g scaffold Movie name:string

现在,我可以做类似的事情

rails g scaffold Movie name:string person_id:integer

相反,但我希望一部电影同时与导演和演员相关联。(下一步是建立一个关联,将多个演员与一部电影联系起来,但我还没有完全做到。)

因此,我前往博客文章使用同一张表创建多个关联,描述了我所需要的内容。这是一个有点旧的帖子,所以现在情况可能已经改变了——我不知道。反正。这就是我改变模型的方式:

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,我打开浏览器并开始注册人员

在此处输入图像描述

是时候开始注册电影了。根据此处此处的先前问题,我必须制作一个迁移脚本才能创建必要的数据库字段。所以这就是我所做的:

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 %>

当我创建一个新电影时,视图显示正常,选择输入工作正常。但是,director 和 actor 字段中的数据不会被持久化。我跑过去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

不幸的是,当我发布新电影时,我得到

人(#70319935588740)预期,得到字符串(#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

我试图在 Rails 控制台中创建一个新电影,如下所示:

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这让我测试了如果我排除了and的引号会发生什么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

它就像一个魅力(为简洁起见省略了输出)。所以整个问题归结为“如何将 Person 作为参数传递给director_idactor_id通过 Web 界面?”

如果我在 Movies 中有一个名为 的字段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"}

然而,rails 明白这continent_id是表中条目的标识符Continent

可悲的是,推断器机制在我原来的情况下不起作用,因为我有两个从表MoviePerson表的关联。我需要以某种方式确保 rails 了解存在从director_id到 a的映射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_idperson_id?谢谢!

4

1 回答 1

0

好的,所以我终于明白了。我认为这不是解决此问题的正确方法,但至少它解决了问题。正如我在更新 2 中提到的,我能够Movie在 irb 中创建一个对象,所以我问了一个问题“如何通过 Web 界面将 Person 作为参数传递给 director_id 和 actor_id?”

根据这里其他地方的消息来源,rails 应该已经理解了has_manybelongs_to类方法。但是,我似乎无法让它工作。

所以我破解了这个create方法,movies_controller.rb如下所示:

def create
  director = Person.where(:id => movie_params[:director_id]).first
  actor = Person.where(:id => movie_params[:actor_id]).first
  @movie = Movie.new(name: movie_params[:name], director_id: director, actor_id: actor)

  respond_to do |format|
    if @movie.save
      format.html { redirect_to @movie, notice: 'Movie was successfully created.' }
      format.json { render action: 'show', status: :created, location: @movie }
    else
      format.html { render action: 'new' }
      format.json { render json: @movie.errors, status: :unprocessable_entity }
    end
  end
end

这当然不像我希望的那样优雅,而且我不认为这是做事的 RoR 方式。不幸的是,这是我迄今为止唯一的工作,但如果其他人可以使用 rails 4 对同一模型进行多重关联,请提醒我!:)

于 2013-08-05T19:40:25.187 回答