我有一个包含选择和文本字段的表单(您可以选择一个
MediaType
或创建一个):
<div>
<%= form.label :media_type_id, style: "display: block" %>
<%= form.collection_select :media_type_id, MediaType.all, :id, :name, :prompt => "Select a Media Type" %>
or create one:
<%= form.text_field :new_media_type_name %>
</div>
模型代码包含支持
new_media_type_name
的内容:
class Medium < ApplicationRecord
has_many :authorships
has_many :authors, through: :authorships
belongs_to :book_genre
belongs_to :media_type
attr_accessor :new_media_type_name
before_save :create_media_type_from_name
def create_media_type_from_name
create_media_type(:name => new_media_type_name) unless new_media_type_name.blank?
end
end
这一切在控制台中都可以正常工作:
irb(main):001> m = Medium.new
=>
#<Medium:0x00007f9c867307d8
...
irb(main):002> m.create_media_type(:name => "Test")
TRANSACTION (1.1ms) BEGIN
MediaType Create (2.1ms) INSERT INTO "media_types" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Test"], ["created_at", "2024-09-29 01:06:50.826184"], ["updated_at", "2024-09-29 01:06:50.826184"]]
TRANSACTION (17.3ms) COMMIT
=>
#<MediaType:0x00007f9c86cb6270
id: 6,
name: "Test",
created_at: Sun, 29 Sep 2024 01:06:50.826184000 UTC +00:00,
updated_at: Sun, 29 Sep 2024 01:06:50.826184000 UTC +00:00>
但是从浏览器运行应用程序不会保存新的
MediaType
。
这是我在运行的输出中看到的内容
rails s
:
Started POST "/media" for 192.168.1.142 at 2024-09-28 17:34:33 -0500
Processing by MediaController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "medium"=>{"title"=>"The New Test", "country"=>"Canada", "published_on"=>"2024-09-05", "media_type_id"=>"", "new_media_type_name"=>"Spaceship", "book_genre_id"=>"3", "summary"=>"The summary", "cover_art_path"=>"", "isbn"=>""}, "commit"=>"Create Medium"}
TRANSACTION (0.8ms) BEGIN
↳ app/controllers/media_controller.rb:26:in `create'
BookGenre Load (1.2ms) SELECT "book_genres".* FROM "book_genres" WHERE "book_genres"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
↳ app/controllers/media_controller.rb:26:in `create'
TRANSACTION (0.9ms) ROLLBACK
↳ app/controllers/media_controller.rb:26:in `create'
Rendering layout layouts/application.html.erb
Rendering media/new.html.erb within layouts/application
MediaType Load (1.3ms) SELECT "media_types".* FROM "media_types"
↳ app/views/media/_form.html.erb:35
BookGenre Load (1.3ms) SELECT "book_genres".* FROM "book_genres" ORDER BY "book_genres"."name" ASC
↳ app/views/media/_form.html.erb:42:in `map'
Rendered media/_form.html.erb (Duration: 48.9ms | Allocations: 8316)
Rendered media/new.html.erb within layouts/application (Duration: 50.5ms | Allocations: 8853)
Rendered layout layouts/application.html.erb (Duration: 85.5ms | Allocations: 20627)
Completed 422 Unprocessable Entity in 181ms (Views: 85.5ms | ActiveRecord: 31.2ms | Allocations: 44624)
问题是,保存失败,浏览器中显示的错误始终是“媒体类型必须存在”。 我认为这意味着它无法创建
Medium
,除非附加到它的 media_type
已经存在。 但我认为这就是模型中 before_save
块的目的。
求求各位大神们,我错过了什么? 瑞安? 瑞恩·贝茨,你在吗?
更新:我发现如果我在表单中选择
media_type
同时输入new_media_type_name
,它会创建新的media_type
并将新的media_type
设置为新媒体中的media_type
参考对象。
accepts_nested_attributes
在模型中创建 setter:
class Medium < ApplicationRecord
# ...
belongs_to :media_type
accepts_nested_attributes_for :media_type,
reject_if: :all_blank?
end
要在表单中创建输入,请使用 fields_for:
<div>
<div class="field">
<%= form.label :media_type_id, style: "display: block" %>
<%= form.collection_select :media_type_id, MediaType.all, :id, :name, prompt: "Select a Media Type" %>
</div>
<fieldset>
<legend>Or create one</legend>
<%= form.fields_for(:media_type) do |media_type_form| %>
<div class="field">
<%= media_type_form.label :name %>
<%= media_type_form.text_input :name %>
</div>
<% end %>
</fieldset>
</div>
然后,您可以通过传递哈希选项将控制器中的嵌套属性列入白名单:
def medium_attributes
params.require(:medium)
.permit(
:foo,
:bar,
:baz,
media_type_attributes: [:name]
)
end