【问题标题】:Rails 4 - trouble with two associations, same modelRails 4 - 两个关联的麻烦,相同的模型
【发布时间】:2023-03-05 04:56:01
【问题描述】:

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

是的,我确实意识到potential risks(方框 1.2.Scaffolding:更快、更容易、更诱人),但我保证在我真正了解什么之前我不会让我的项目公开在引擎盖下进行。现在我更多的是在“为我的下一个超级骗子项目评估技术”模式。

这是我目前所得到的:

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

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

rails g scaffold Movie name:string person_id:integer

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

所以,我前往博客文章Creating Multiple Associations With the Same Table,描述了我所需要的内容。这是一个有点旧的帖子,所以现在情况可能已经改变了——我不知道。反正。这就是我改变模型的方式:

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

通过在控制台中运行rails s 启动WEBrick,我打开浏览器并开始注册人员

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

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"=>"???", "电影"=>{"名字"=>"爱情喜剧", "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_idactor_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?”

如果我在 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_idContinent 表中条目的标识符。

遗憾的是,推断机制在我原来的情况下不起作用,因为我有两个从 MoviePerson 表的关联。我需要以某种方式确保 rails 了解存在从 director_idPerson 的映射。

更新 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?谢谢!

【问题讨论】:

  • 您必须添加accepts_nested_attributes_for :actor 和电影模型中的导演相同;)
  • 我认为你应该使用has_and_belongs_to_many关系,因为电影有很多演员,演员可以出现在很多电影中
  • 您在控制器中获得了哪些参数?你在4号线吗?您还必须在 rails 控制器中更新您的私有方法。在 rails 4 中,只允许使用允许的参数。 @MrYoshiiji 我不认为accepts_nested_attributes 是必要的..
  • @Mattherick:哦,忘了提 - 我正在运行 rails 4。根据你的建议,我尝试通过说 params.require(:movie).permit(:name, :director, :actor) 来允许 :director 和 :actor,但现在我得到以下信息错误:预期人(#70318440095780),得到字符串(#70318417177660)
  • 添加调试器并检查您的正确参数。也许您必须在您的许可方法中将 :director 更改为 :director_id 和 :actor 更改为 :actor_id。

标签: ruby-on-rails scaffolding rails-migrations


【解决方案1】:

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

根据hereelsewhere 的来源,rails 应该理解has_manybelongs_to 类方法。但是,我似乎无法让它工作。

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

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 对同一模型进行多重关联,请务必提醒我! :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-23
    • 2015-06-08
    • 2015-05-27
    • 2016-04-14
    • 1970-01-01
    相关资源
    最近更新 更多