如何避免在 Rails 嵌套表单中为 user_id 使用隐藏字段
How to avoid using a hidden field for user_id in a Rails nested form
我觉得这在Rails中应该很容易做到,但是Rails中所有嵌套形式的例子都没有考虑到大多数嵌套形式也需要通过嵌套表单创建新对象时传递 current_user。
目前我唯一能让它工作的方法是传递一个隐藏字段,例如 <%= form.hidden_field :user_id, value: current_user.id %>
.
对于我的具体示例,我有一个名为 "Result" 的模型,其中有很多 "Lessons",我想通过结果表单创建新课程而不传递隐藏的 :user_id
.
这似乎不安全,因为有人可以在浏览器中编辑该隐藏字段,然后提交表单,从而将提交与其他用户相关联。 current_user.id 似乎是您不想作为隐藏字段嵌入到 html 中的类型。
那么如何在不将隐藏字段放入表单中的情况下创建嵌套对象与 current_user 之间的关联?
仅供参考,我正在使用 GoRails nested form with stimulus style javascript to add and remove lessons from the result form. (Here's the source code for that example。)以下是我的代码的相关部分:
models/result.rb
class Result < ApplicationRecord
belongs_to :user
has_many :lessons, inverse_of: :result
accepts_nested_attributes_for :lessons, reject_if: :all_blank, allow_destroy: true
end
models/lesson.rb
class Lesson < ApplicationRecord
belongs_to :user
belongs_to :result
end
controllers/results_controller.rb
class ResultsController < ApplicationController
before_action :set_result, only: [:show, :edit, :update, :destroy]
def new
@result = Result.new
@result.lessons.new
end
def create
@result = current_user.results.new(result_params)
respond_to do |format|
if @result.save
format.html { redirect_to @result, notice: 'Result was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_result
@result = Result.find(params[:id])
end
def result_params
params.require(:result).permit(:prediction_id, :post_mortem, :correct,
lessons_attributes: [:user_id, :id, :summary, :_destroy])
end
end
controllers/lessons_controller.rb
class LessonsController < ApplicationController
before_action :set_lesson, only: [:show, :edit, :update, :destroy]
# GET /lessons/new
def new
@lesson = Lesson.new
end
def create
@lesson = current_user.lessons.new(lesson_params)
respond_to do |format|
if @lesson.save
format.html { redirect_to @lesson, notice: 'Lesson was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_lesson
@lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:result_id, :summary)
end
end
views/results/_form.html.erb
<%= form_with(model: result, local: true) do |form| %>
<h3>Lessons</h3>
<div data-controller="nested-form">
<template data-target="nested-form.template">
<%= form.fields_for :lessons, Lesson.new, child_index: 'NEW_RECORD' do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
</template>
<%= form.fields_for :lessons do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
<div class="pt-4" data-target="nested-form.links">
<%= link_to "Add Lesson", "#",
data: { action: "click->nested-form#add_association" } %>
</div>
</div>
<div class="form-submit">
<%= form.submit "Save" %>
</div>
<% end %>
views/results/_lesson_fields.html.erb
<%= content_tag :div, class: "nested-fields", data: { new_record: form.object.new_record? } do %>
# This hidden field seems unsafe!
<%= form.hidden_field :user_id, value: current_user.id %>
<div class="pb-8">
<%= form.text_area :summary %>
<%= link_to "Remove", "#",
data: { action: "click->nested-form#remove_association" } %>
</div>
<%= form.hidden_field :_destroy %>
<% end %>
我确定这是 Rails 中的一个常见问题,但我在网上找不到任何将 user_id 作为嵌套字段示例的一部分的教程。非常感谢任何帮助!
我认为没有很好的方法可以在视图之外自动处理这个问题。您要么必须将值注入参数,要么可能在 Lesson 中的 user
关联上使用 default
从 Record 的用户 (belongs_to :user, default: -> { result.user }
) 设置它。在这些场景中,我通常会跳出默认 Rails 流程并使用 PORO、表单对象、服务对象等
就个人而言,由于设置 current_user id 是控制器应该关心的事情,我会遍历所有课程并在那里设置 user_id 值。
def create
@result = current_user.results.new(result_params)
@result.lessons.each do |lesson|
lesson.user ||= current_user if lesson.new_record?
end
... the rest ...
拥有隐藏字段存在安全风险,有人可以对其进行编辑。我也不喜欢更改参数哈希。
像这样构建表单
<%= form.fields_for :lessons, lesson_for_form(current_user.id) do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
删除您添加的隐藏 user_id 字段
更新您的 result.rb 文件
class Result < ApplicationRecord
def lesson_for_form(user_id)
collection = lessons.where(user_id: user_id)
collection.any? ? collection : lessons.build(user_id: user_id)
end
end
我觉得这在Rails中应该很容易做到,但是Rails中所有嵌套形式的例子都没有考虑到大多数嵌套形式也需要通过嵌套表单创建新对象时传递 current_user。
目前我唯一能让它工作的方法是传递一个隐藏字段,例如 <%= form.hidden_field :user_id, value: current_user.id %>
.
对于我的具体示例,我有一个名为 "Result" 的模型,其中有很多 "Lessons",我想通过结果表单创建新课程而不传递隐藏的 :user_id
.
这似乎不安全,因为有人可以在浏览器中编辑该隐藏字段,然后提交表单,从而将提交与其他用户相关联。 current_user.id 似乎是您不想作为隐藏字段嵌入到 html 中的类型。
那么如何在不将隐藏字段放入表单中的情况下创建嵌套对象与 current_user 之间的关联?
仅供参考,我正在使用 GoRails nested form with stimulus style javascript to add and remove lessons from the result form. (Here's the source code for that example。)以下是我的代码的相关部分:
models/result.rb
class Result < ApplicationRecord
belongs_to :user
has_many :lessons, inverse_of: :result
accepts_nested_attributes_for :lessons, reject_if: :all_blank, allow_destroy: true
end
models/lesson.rb
class Lesson < ApplicationRecord
belongs_to :user
belongs_to :result
end
controllers/results_controller.rb
class ResultsController < ApplicationController
before_action :set_result, only: [:show, :edit, :update, :destroy]
def new
@result = Result.new
@result.lessons.new
end
def create
@result = current_user.results.new(result_params)
respond_to do |format|
if @result.save
format.html { redirect_to @result, notice: 'Result was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_result
@result = Result.find(params[:id])
end
def result_params
params.require(:result).permit(:prediction_id, :post_mortem, :correct,
lessons_attributes: [:user_id, :id, :summary, :_destroy])
end
end
controllers/lessons_controller.rb
class LessonsController < ApplicationController
before_action :set_lesson, only: [:show, :edit, :update, :destroy]
# GET /lessons/new
def new
@lesson = Lesson.new
end
def create
@lesson = current_user.lessons.new(lesson_params)
respond_to do |format|
if @lesson.save
format.html { redirect_to @lesson, notice: 'Lesson was successfully created.' }
else
format.html { render :new }
end
end
end
private
def set_lesson
@lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:result_id, :summary)
end
end
views/results/_form.html.erb
<%= form_with(model: result, local: true) do |form| %>
<h3>Lessons</h3>
<div data-controller="nested-form">
<template data-target="nested-form.template">
<%= form.fields_for :lessons, Lesson.new, child_index: 'NEW_RECORD' do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
</template>
<%= form.fields_for :lessons do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
<div class="pt-4" data-target="nested-form.links">
<%= link_to "Add Lesson", "#",
data: { action: "click->nested-form#add_association" } %>
</div>
</div>
<div class="form-submit">
<%= form.submit "Save" %>
</div>
<% end %>
views/results/_lesson_fields.html.erb
<%= content_tag :div, class: "nested-fields", data: { new_record: form.object.new_record? } do %>
# This hidden field seems unsafe!
<%= form.hidden_field :user_id, value: current_user.id %>
<div class="pb-8">
<%= form.text_area :summary %>
<%= link_to "Remove", "#",
data: { action: "click->nested-form#remove_association" } %>
</div>
<%= form.hidden_field :_destroy %>
<% end %>
我确定这是 Rails 中的一个常见问题,但我在网上找不到任何将 user_id 作为嵌套字段示例的一部分的教程。非常感谢任何帮助!
我认为没有很好的方法可以在视图之外自动处理这个问题。您要么必须将值注入参数,要么可能在 Lesson 中的 user
关联上使用 default
从 Record 的用户 (belongs_to :user, default: -> { result.user }
) 设置它。在这些场景中,我通常会跳出默认 Rails 流程并使用 PORO、表单对象、服务对象等
就个人而言,由于设置 current_user id 是控制器应该关心的事情,我会遍历所有课程并在那里设置 user_id 值。
def create
@result = current_user.results.new(result_params)
@result.lessons.each do |lesson|
lesson.user ||= current_user if lesson.new_record?
end
... the rest ...
拥有隐藏字段存在安全风险,有人可以对其进行编辑。我也不喜欢更改参数哈希。
<%= form.fields_for :lessons, lesson_for_form(current_user.id) do |lesson| %>
<%= render "lesson_fields", form: lesson %>
<% end %>
删除您添加的隐藏 user_id 字段
更新您的 result.rb 文件 class Result < ApplicationRecord
def lesson_for_form(user_id)
collection = lessons.where(user_id: user_id)
collection.any? ? collection : lessons.build(user_id: user_id)
end
end