我在 Rails 应用程序中嵌套了资源,基本上一个 Project
有 Targets
,我认为创建关系的最简单方法是在我的路线.rb:
resource :projects do
resource :targets
end
我的模型也几乎匹配:
- Project.rb
class Project < ApplicationRecord
has_many :targets
end
- Target.rb
class Target < ApplicationRecord
has_one :project
end
运行rake paths
向我展示了我所期望的:
project_targets GET /projects/:project_id/targets(.:format) targets#index
POST /projects/:project_id/targets(.:format) targets#create
new_project_target GET /projects/:project_id/targets/new(.:format) targets#new
edit_project_target GET /projects/:project_id/targets/:id/edit(.:format) targets#edit
project_target GET /projects/:project_id/targets/:id(.:format) targets#show
PATCH /projects/:project_id/targets/:id(.:format) targets#update
PUT /projects/:project_id/targets/:id(.:format) targets#update
DELETE /projects/:project_id/targets/:id(.:format) targets#destroy
projects GET /projects(.:format) projects#index
POST /projects(.:format) projects#create
new_project GET /projects/new(.:format) projects#new
edit_project GET /projects/:id/edit(.:format) projects#edit
project GET /projects/:id(.:format) projects#show
PATCH /projects/:id(.:format) projects#update
PUT /projects/:id(.:format) projects#update
DELETE /projects/:id(.:format) projects#destroy
完成此操作后,我的创建/编辑表单不再适用于 target
,因此我必须调整 form_for
的第一行:
<%= form_for @target do |f| %>
to
<%= form_for @target, url: project_targets_path do |f| %>
注意我必须显式声明 url
Target
Controller 中的 create 方法非常基本:
def create
@target = Target.create(target_params)
if @target.valid?
redirect_to project_target_path(id: @target.id)
else
flash[:errors] = @target.errors.full_messages
redirect_to new
end
end
我尝试创建目标成功,但根据数据库架构,该目标没有分配给它的 project_id
:
create_table :targets do |t|
t.string :domain
t.boolean :investigated, default: false
t.boolean :research, default: false
t.integer :project_id
t.timestamps
end
这是我在日志中看到的内容,显然 project_id
作为 URL 的一部分传递,但它在创建时并未与新目标关联。
Started POST "/projects/1/targets" for ::1 at 2020-03-13 19:19:57 -0400
Processing by TargetsController#create as HTML
Parameters: {"authenticity_token"=>"jLzQmpsxMRW4z66WguFVwZcLnMxFJYIy86EDfru6fIhysWDU/fd6yq5HV2uv1Z3TICGQSAXZDll66DwizReWaQ==", "target"=>{"domain"=>"2-test.com"}, "commit"=>"Create", "project_id"=>"1"}
(0.1ms) begin transaction
↳ app/controllers/targets_controller.rb:11:in `create'
Target Create (0.8ms) INSERT INTO "targets" ("domain", "created_at", "updated_at") VALUES (?, ?, ?) [["domain", "2-test.com"], ["created_at", "2020-03-13 23:19:57.097233"], ["updated_at", "2020-03-13 23:19:57.097233"]]
↳ app/controllers/targets_controller.rb:11:in `create'
(1.1ms) commit transaction
↳ app/controllers/targets_controller.rb:11:in `create'
我的设置不正确吗?如何解决此问题,以便在创建新目标时包含 project_id
?我想保持这个 RESTful,并且如果可能的话,当其中包含 project_id 时不传递隐藏字段。
最佳答案
将表单设置为:
<%= form_for [@project, @target] do |f| %>
虽然您可以显式传递网址:
<%= form_for @target, url: project_targets_path(@project) do |f| %>
如果您在部分共享表单,这将破坏 edit
和 update
操作,因为操作属性应指向 /projects/:project_id/更新时的targets/:id
。优先选择约定而非配置。
您的创建方法在很多方面也被破坏了。
class TargetsController < ApplicationController
before_action :set_project
before_action :set_target, except: [:new, :index]
# POST /projects/1/targets
def create
# building the new record off the parent sets the project_id
@target = @project.targets.new(target_params)
if @target.save
redirect_to [@project, @target]
else
flash[:errors] = @target.errors.full_messages
# When a record is invalid always render - never redirect.
render :new
end
end
private
def set_project
@project = Project.find(params[:project_id])
end
# ...
end
if @project.valid?
只是检查应用程序验证是否已通过 - 而不是记录是否实际保存到数据库中。检查 @target.save
或 @target.persisted?
的返回值。
请注意,:project_id
不应包含在您的参数白名单中,因为它是通过 url 传递的,而不是通过批量分配传递的。嵌套路由实际上与强参数没有任何关系。
您还应该阅读 shallow nesting因为您很可能不需要嵌套成员路由,并且它大大降低了复杂性。
关于ruby-on-rails - Rails 6 具有强参数的嵌套资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60678430/