ruby-on-rails - Rails 6 具有强参数的嵌套资源

标签 ruby-on-rails ruby

我在 Rails 应用程序中嵌套了资源,基本上一个 ProjectTargets,我认为创建关系的最简单方法是在我的路线.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| %>

如果您在部分共享表单,这将破坏 editupdate 操作,因为操作属性应指向 /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/

相关文章:

ruby-on-rails - Redis 发布/订阅实现

ruby-on-rails - 单表继承(STI)场景中的类型转换基类模型

ruby-on-rails - 从 rake 任务调用 Controller

ruby-on-rails - RoR - #<Movie :0x007fa43a0629e0> 的未定义方法 `movie'

ruby-on-rails - 我们可以从 View 调用 Controller 的方法吗(理想情况下我们从 helper 调用)?

ruby-on-rails - 使 Mongrel/WEBRick 提供具有 future 过期 header 的静态 Assets

javascript - 使用 Rails,如何在应用程序的特定页面上执行 javascript 文件?

ruby-on-rails - Rails 更新三向连接表

ruby-on-rails - Rails - 使用 Capistrano 部署后需要重新启动 Nginx?

javascript - 从第三方网站的 script/include 标签配置 Javascript 的最佳方法是什么?