在 Rails 5 中,如何使用单个控制器操作在单独的模型中创建记录?
In Rails 5 how can I create records in separate models with a single controller action?
我正在学习 Rails 创建詹姆斯·邦德电影数据库的教程。我有两个模型,movie.rb 和 theme_song.rb。两者有如下关联:
class Movie < ApplicationRecord
has_one :theme_song
end
class ThemeSong < ApplicationRecord
belongs_to :movie
end
我也在我的 routes.rb 文件中代表了这个协会:
resources :movies do
resources :theme_songs
end
我需要允许用户将其他电影的详细信息添加到数据库中,包括一般电影信息和有关主题曲的信息。一般信息属于Movie模型,主题曲信息属于ThemeSong模型,如下迁移所示:
create_table :movies do |t|
t.string :title
t.datetime :release_date
t.text :plot
t.timestamps
end
create_table :theme_songs do |t|
t.string :song
t.string :artist
t.references :movie, foreign_key: true
t.timestamps
end
我有一个 movies_controller.rb 控制器和一个链接到控制器的 new.html.erb 视图。 new.html.erb 视图中编码了以下形式:
<%= form_with model: @movie, local: true do |form| %>
<p><strong>Movie Details</strong></p>
<p>
<%= form.label 'Title:' %>
<%= form.text_field :title %>
</p>
<p>
<%= form.label 'Release date:' %>
<%= form.text_field :release_date %>
</p>
<p>
<%= form.label 'Plot:' %>
<%= form.text_area :plot %>
</p>
<p><strong>Soundtrack</strong></p>
<p>
<%= form.label 'Theme song:' %>
<%= form.text_area :song %>
</p>
<p>
<%= form.label 'Artist:' %>
<%= form.text_area :artist %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
我需要用户能够填写该表单并提交,然后我的控制器创建操作以在 Movie 中添加一条包含一般信息的新记录,在 ThemeSong 中添加一条包含配乐信息的新记录,同时关联中链接的记录。
这是我目前在 movies_controller.rb:
的相关方法中得到的代码
def create
@movie = Movie.new(movie_params)
if @movie.save
redirect_to @movie
else
render 'new'
end
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist)
end
谁能告诉我 create/movie_params 方法中需要包含哪些代码?或者,如果我以完全错误的方式进行处理,如果是这样,我应该如何实施?
我正在使用 Rails 5.1.4
您需要使用的是 accepts_nested_attributes_for
,它允许您在 movie
表单中为 theme_song
嵌套表单:
将其添加到您的 Movie
型号:
class Movie < ApplicationRecord
has_one :theme_song
accepts_nested_attributes_for :theme_song
end
在你的电影控制器中:
def new
@movie = Movie.new
@movie.build_theme_song
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist, theme_song_attributes: [:song, :artist])
end
最后,在您的 movie
表单中使用 fields_for
帮助程序为电影的 theme_song
:
添加字段
<%= form_for @movie do |f| %>
theme song:
<ul>
<%= f.fields_for @movie.theme_song do |theme_song_form| %>
<li>
<%= theme_song_form.label :song %>
<%= theme_song_form.text_field :song %>
...
</li>
<% end %>
</ul>
<% end %>
现在,当您提交表单时,它将为该电影创建 movie
记录和 theme_song
记录。
没有为 theme_song 模型提供信息,需要创建哪些字段或哪些数据,但您可能不希望两个表中的数据重复。
您没有走错路,使用 accepts_nested_attributes 将两组数据添加到单个表单可能是最简单的方法。然后告诉您的 movies_controller 参数也接受嵌套特征。然后以相应的方式保存数据是rails魔术。
1 为电影模型添加嵌套选项
class Movie < ApplicationRecord
has_one :theme_song
accepts_nested_attributes_for :theme_song
end
2 将表格嵌套在一起
<%= form_with model: @movie, local: true do |form| %>
<p><strong>Movie Details</strong></p>
<p>
<%= form.label 'Title:' %>
<%= form.text_field :title %>
</p>
<p>
<%= form.label 'Release date:' %>
<%= form.text_field :release_date %>
</p>
<p>
<%= form.label 'Plot:' %>
<%= form.text_area :plot %>
</p>
<p><strong>Soundtrack</strong></p>
<%= form.fields_for :theme_song do |theme_song| %>
<p>
<%= theme_song.label 'Theme song:' %>
<%= theme_song.text_area :song %>
</p>
<p>
<%= theme_song.label 'Artist:' %>
<%= theme_song.text_area :artist %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
3 更新控制器参数 + 创建新对象
def create
@movie = Movie.new(movie_params)
if @movie.save
redirect_to @movie
else
render 'new'
end
end
def new
@movie = Movie.new
@movie.build_theme_song
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist, theme_song_attributes: [:song, :artist, :id])
end
这里有两点需要注意,第一是在新动作中你需要确保构建嵌套记录。其次嵌套的属性以数组的形式添加到params中,必须这样。
旁注
使用此方法有其局限性。你应该知道,这意味着如果两部不同的电影使用同一首主题曲,你将有 2 个电影记录和 2 个主题曲记录。这可能会使 slower/harder 变成 "show me all the movies that this song is in",因为这样你就必须考虑用户在按名称搜索时的错误和拼写。你可能会发现,在未来,最好有一个 Movie 记录,一个 Theme Song 记录和一个 MovieThemeSongs 记录,它本质上是将两者结合在一起。
我正在学习 Rails 创建詹姆斯·邦德电影数据库的教程。我有两个模型,movie.rb 和 theme_song.rb。两者有如下关联:
class Movie < ApplicationRecord
has_one :theme_song
end
class ThemeSong < ApplicationRecord
belongs_to :movie
end
我也在我的 routes.rb 文件中代表了这个协会:
resources :movies do
resources :theme_songs
end
我需要允许用户将其他电影的详细信息添加到数据库中,包括一般电影信息和有关主题曲的信息。一般信息属于Movie模型,主题曲信息属于ThemeSong模型,如下迁移所示:
create_table :movies do |t|
t.string :title
t.datetime :release_date
t.text :plot
t.timestamps
end
create_table :theme_songs do |t|
t.string :song
t.string :artist
t.references :movie, foreign_key: true
t.timestamps
end
我有一个 movies_controller.rb 控制器和一个链接到控制器的 new.html.erb 视图。 new.html.erb 视图中编码了以下形式:
<%= form_with model: @movie, local: true do |form| %>
<p><strong>Movie Details</strong></p>
<p>
<%= form.label 'Title:' %>
<%= form.text_field :title %>
</p>
<p>
<%= form.label 'Release date:' %>
<%= form.text_field :release_date %>
</p>
<p>
<%= form.label 'Plot:' %>
<%= form.text_area :plot %>
</p>
<p><strong>Soundtrack</strong></p>
<p>
<%= form.label 'Theme song:' %>
<%= form.text_area :song %>
</p>
<p>
<%= form.label 'Artist:' %>
<%= form.text_area :artist %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
我需要用户能够填写该表单并提交,然后我的控制器创建操作以在 Movie 中添加一条包含一般信息的新记录,在 ThemeSong 中添加一条包含配乐信息的新记录,同时关联中链接的记录。
这是我目前在 movies_controller.rb:
的相关方法中得到的代码def create
@movie = Movie.new(movie_params)
if @movie.save
redirect_to @movie
else
render 'new'
end
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist)
end
谁能告诉我 create/movie_params 方法中需要包含哪些代码?或者,如果我以完全错误的方式进行处理,如果是这样,我应该如何实施?
我正在使用 Rails 5.1.4
您需要使用的是 accepts_nested_attributes_for
,它允许您在 movie
表单中为 theme_song
嵌套表单:
将其添加到您的 Movie
型号:
class Movie < ApplicationRecord
has_one :theme_song
accepts_nested_attributes_for :theme_song
end
在你的电影控制器中:
def new
@movie = Movie.new
@movie.build_theme_song
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist, theme_song_attributes: [:song, :artist])
end
最后,在您的 movie
表单中使用 fields_for
帮助程序为电影的 theme_song
:
<%= form_for @movie do |f| %>
theme song:
<ul>
<%= f.fields_for @movie.theme_song do |theme_song_form| %>
<li>
<%= theme_song_form.label :song %>
<%= theme_song_form.text_field :song %>
...
</li>
<% end %>
</ul>
<% end %>
现在,当您提交表单时,它将为该电影创建 movie
记录和 theme_song
记录。
没有为 theme_song 模型提供信息,需要创建哪些字段或哪些数据,但您可能不希望两个表中的数据重复。
您没有走错路,使用 accepts_nested_attributes 将两组数据添加到单个表单可能是最简单的方法。然后告诉您的 movies_controller 参数也接受嵌套特征。然后以相应的方式保存数据是rails魔术。
1 为电影模型添加嵌套选项
class Movie < ApplicationRecord
has_one :theme_song
accepts_nested_attributes_for :theme_song
end
2 将表格嵌套在一起
<%= form_with model: @movie, local: true do |form| %>
<p><strong>Movie Details</strong></p>
<p>
<%= form.label 'Title:' %>
<%= form.text_field :title %>
</p>
<p>
<%= form.label 'Release date:' %>
<%= form.text_field :release_date %>
</p>
<p>
<%= form.label 'Plot:' %>
<%= form.text_area :plot %>
</p>
<p><strong>Soundtrack</strong></p>
<%= form.fields_for :theme_song do |theme_song| %>
<p>
<%= theme_song.label 'Theme song:' %>
<%= theme_song.text_area :song %>
</p>
<p>
<%= theme_song.label 'Artist:' %>
<%= theme_song.text_area :artist %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
3 更新控制器参数 + 创建新对象
def create
@movie = Movie.new(movie_params)
if @movie.save
redirect_to @movie
else
render 'new'
end
end
def new
@movie = Movie.new
@movie.build_theme_song
end
private
def movie_params
params.require(:movie).permit(:title, :release_date, :plot, :song, :artist, theme_song_attributes: [:song, :artist, :id])
end
这里有两点需要注意,第一是在新动作中你需要确保构建嵌套记录。其次嵌套的属性以数组的形式添加到params中,必须这样。
旁注
使用此方法有其局限性。你应该知道,这意味着如果两部不同的电影使用同一首主题曲,你将有 2 个电影记录和 2 个主题曲记录。这可能会使 slower/harder 变成 "show me all the movies that this song is in",因为这样你就必须考虑用户在按名称搜索时的错误和拼写。你可能会发现,在未来,最好有一个 Movie 记录,一个 Theme Song 记录和一个 MovieThemeSongs 记录,它本质上是将两者结合在一起。