【发布时间】: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,我打开浏览器并开始注册人员
是时候开始注册电影了。根据之前的问题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 %>
当我创建一个新电影时,视图显示正常,选择输入工作正常。但是,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_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
它就像一个魅力(为简洁起见省略了输出)。所以整个问题归结为 “如何通过网络界面将 Person 作为参数传递给 director_id 和 actor_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_id 是 Continent 表中条目的标识符。
遗憾的是,推断机制在我原来的情况下不起作用,因为我有两个从 Movie 到 Person 表的关联。我需要以某种方式确保 rails 了解存在从 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?谢谢!
【问题讨论】:
-
您必须添加
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