有时候,Rails让您开心地大笑,而让您绝望地哭泣。这是后者之一... 在我正在构建的一个更复杂的应用程序上为此苦苦挣扎之后,我构建了一个简单的应用程序来隔离我遇到的问题。 总结-帖子中有评论和标签。一个标签可以属于多个帖子,而一个帖子可以具有多个标签。我想要一个嵌套表格,让我可以一口气做到这一点。我正在使用复选框,因为在更复杂的应用程序中需要使用它们。
class Comment < ApplicationRecord
belongs_to :post
end
class Post < ApplicationRecord
has_many :comments
has_many :post_tags
has_many :tags, through: :post_tags
accepts_nested_attributes_for :comments
accepts_nested_attributes_for :tags, update_only: true
end
class PostTag < ApplicationRecord
belongs_to :post
belongs_to :tag
end
<%= form_with(model: post, local: true) do |form| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<h2>Comments</h2>
<%= form.fields_for :comments do |fc| %>
<%= fc.label :body %>
<%= fc.text_field :body %>
<br />
<% end %>
<h2>Tags</h2>
<%= form.fields_for :tags do |ft| %>
<%= ft.collection_check_boxes(:tag, Tag.all, :id, :tag) do |cb| %>
<%= cb.label %>
<%= cb.check_box %>
<% end %>
<br />
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
标准脚手架-除外
def new
@post = Post.new
@post.comments.build
@post.tags.build
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:title, comments_attributes: [:body, :id], tags_attributes: [{:tag=>[]}, :id])
end
这是collection_check_boxes生成的HTML
<input type="hidden" name="post[tags_attributes][0][tag][]" value="" />
<label for="post_tags_attributes_0_tag_1">tag 1</label>
<input type="checkbox" value="1" name="post[tags_attributes][0][tag][]" id="post_tags_attributes_0_tag_1" />
<label for="post_tags_attributes_0_tag_2">tag 2</label>
<input type="checkbox" value="2" name="post[tags_attributes][0][tag][]" id="post_tags_attributes_0_tag_2" />
<label for="post_tags_attributes_0_tag_3">tag 3</label>
<input type="checkbox" value="3" name="post[tags_attributes][0][tag][]" id="post_tags_attributes_0_tag_3" />
<label for="post_tags_attributes_0_tag_4">tag 4</label>
<input type="checkbox" value="4" name="post[tags_attributes][0][tag][]" id="post_tags_attributes_0_tag_4" />
<br />
<input type="hidden" value="3" name="post[tags_attributes][0][id]" id="post_tags_attributes_0_id" />
这是通过的
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0eFWbwi3J6gHnyys6+V/aVymheWE37RWtqq0xm46/umASeyTqj6hTqmTrL4DdvV48yFmMgOtiiYorswTtyCbyw==",
"post"=>{"title"=>"test post", "comments_attributes"=>{"0"=>{"body"=>"test comment", "id"=>"1"}},
"tags_attributes"=>{"0"=>{"tag"=>["", "1", "4"], "id"=>"3"}}}, "commit"=>"Update Post", "id"=>"1"}
问题出在tag_attribute上。当更新运行时,它将使用名称“ [”,“ 1”,“ 4”]”更新标签条目3,而不是更新表。我不知道为什么要选择id为“ 3”。 您可能希望Rails能够自动完成所有工作。当它起作用时,它是甜食。如果没有,那就太令人沮丧了。 关于我要去哪里的任何建议?我的怀疑在于collection_check_boxes或params.require。
非常感谢。
答案 0 :(得分:0)
_form.html.erb:
<%= form_with(model: post, local: true) do |form| %>
<!-- ... -->
<!-- ... -->
<%= form.collection_check_boxes(:tag_ids, Tag.all, :id, :tag) do |cb| %>
<%= cb.label %>
<%= cb.check_box %>
<% end %>
<% end %>
posts_controller.rb:
# ...
# ...
def post_params
params.require(:post).permit(
:title,
comments_attributes: [:body, :id],
tag_ids: []
)
end
无论何时考虑如何正确设置表单(即使具有嵌套属性),我通常通常都在rails console
中进行。因此,请查看下面的有趣技巧,您会立即理解我的意思:)
# rails console
post = Post.first
# hmm I wonder what should we put inside the .update() below, for us to be able to set the associated tags?
post.update(...)
# ok, should be easy enough, let's try below?
post.update(tags: [Tag.find(1), Tag.find(3)])
# cool, above works right?, except... that we cannot use it because we are simulating `params` values,
# ... and `params` values do not have record Objects! but only primitives such as `String` and `Integer`
# hmm... so... why not use the primary keys, right? Let's try below
post.update(tag_ids: [1, 3])
很酷,终于成功了! :)
基本上,那么现在我们只需要以某种方式将上面的tag_ids: [1, 3]
转换为表单的输入字段,然后我们应该期望像下面这样具有输入字段的name
(请注意,上面的TL; DR应该已经看起来像这样
<input type='checkbox' name='post[tag_ids][]' value='1' checked>
<input type='checkbox' name='post[tag_ids][]' value='2'>
<input type='checkbox' name='post[tag_ids][]' value='3' checked>
<input type='checkbox' name='post[tag_ids][]' value='4'>
<input type='checkbox' name='post[tag_ids][]' value='9'>
<!-- OTHER REMAINING TAG IDS HERE. ETC... -->