无法使用带有strong_parameters和嵌套属性的POST更新或创建记录

时间:2015-01-21 03:34:38

标签: ruby-on-rails ruby-on-rails-4 rails-activerecord

当我从表单发布POST时,我无法弄清楚为什么我的数据库记录没有得到更新,或者创建了新记录。

我能够手动填充数据库并创建关系:

contact = Contact.first
command = Command.find(3)
contact.host_notification_commands << command

我也可以在表单中加载此数据信息。我无法弄清楚的是如何更新或添加新记录。到目前为止我尝试过的所有东西都失败了。

我有3个模型 - CommandContact和加入CommandsContactcommands_contacts联接表正在支持一个额外的属性:notification_type,该属性可以设置为主机服务,以及我的Contact模型设置了2个额外的关系,我可以访问:host_notification_commands:service_notification_commands。这使我能够执行Contact.fist.host_notification_commandsContact.find(3).service_notification_commands.

等操作

似乎没有UPDATEINSERT查询从控制器启动,当我正确POST时,我无法弄清楚如何调试它。

模型

class Command < ActiveRecord::Base

  has_many :commands_contacts
  has_many :contacts, :through => :commands_contacts

end

class Contact < ActiveRecord::Base

    has_many :commands_contacts
    has_many :commands, :through => :commands_contacts

    has_many :host_notification_commands, -> { where commands_contacts: { :notification_type => 'host' } },
            :through => :commands_contacts,
            :class_name => 'Command', 
            :source => :command

    has_many :service_notification_commands, -> { where commands_contacts: { notification_type: 'service' } },
            :through => :commands_contacts,
            :class_name => 'Command', 
            :source => :command

    accepts_nested_attributes_for :commands, :host_notification_commands, :service_notification_commands

end

class CommandsContact < ActiveRecord::Base

    belongs_to :command
    belongs_to :contact

    accepts_nested_attributes_for :command

end

此后一切都崩溃了。

控制器

由于我使用的是accepts_nested_attributes_for,我必须将_attributes附加到我的嵌套对象的名称 - :host_notification_commands:service_notification_commands。我将改变我的表单以这种方式提交,但简单的重新分配工作是为了一个例子。

def update
    contact = Contact.find_by_id(params[:id])
    contact.update(safe_params)
end    

private
def safe_params

    params[:contact][:host_notification_commands_attributes] = params[:contact][:host_notification_commands]
    params[:contact][:service_notification_commands_attributes] = params[:contact][:service_notification_commands]

    params.require(:contact)
        .permit(:contact_name, :host_notification_commands_attributes => [:id, :command_name, :command_line, :command_description],
                :service_notification_commands_attributes => [:id, :command_name, :command_line, :command_description])
end

更新现有记录结果:

#<ActiveRecord::RecordNotFound: Couldn't find Command with ID=2 for Contact with ID=1>

当然它不存在!我正在努力建立这种关系!

添加一个新的,我得到:

#<ActiveRecord::RecordNotFound: Couldn't find Command with ID=1 for Contact with ID=>

绝对正确。用户甚至还没有创建,并且没有与命令建立关系,为什么Rails试图找到它?

我也没有看到在rails控制台中记录任何更新或插入查询,所以我想它甚至没有达到那一点......

D, [2015-01-20T18:01:30.336669 #95542] DEBUG -- :    (0.1ms)  BEGIN
D, [2015-01-20T18:01:30.338971 #95542] DEBUG -- :   Command Load (0.3ms)  SELECT `commands`.* FROM `commands` INNER JOIN `commands_contacts` ON `commands`.`id` = `commands_contacts`.`command_id` WHERE `commands_contacts`.`contact_id` = 1 AND `commands_contacts`.`notification_type` = 'host' AND `commands`.`id` IN (1, 3)
D, [2015-01-20T18:01:30.340555 #95542] DEBUG -- :   Command Load (0.2ms)  SELECT `commands`.* FROM `commands` INNER JOIN `commands_contacts` ON `commands`.`id` = `commands_contacts`.`command_id` WHERE `commands_contacts`.`contact_id` = 1 AND `commands_contacts`.`notification_type` = 'service' AND `commands`.`id` IN (4, 2)
D, [2015-01-20T18:01:30.341501 #95542] DEBUG -- :    (0.1ms)  ROLLBACK
#<ActiveRecord::RecordNotFound: Couldn't find Command with ID=2 for Contact with ID=1>
Completed 200 OK in 11ms (Views: 0.4ms | ActiveRecord: 0.9ms)

我在这里错过了什么?

编辑:我想我可以放弃使用strong_parameters的想法,解析所有POST参数,然后手动填充数据库,但那不是很Rails-y。

编辑#2:包括发布到控制器的参数。

来自表格

params[:contact]数据
{
                     "contact_name" => "joe-user",
       "host_notification_commands" => [
        [0] {
                             "id" => 1,
                   "command_name" => "host-notify-by-email",
                   "command_line" => "/usr/local/bin/host-notify",
            "command_description" => "Host Alert",
                     "created_at" => "2015-01-19T17:24:12.000Z",
                     "updated_at" => "2015-01-21T03:29:03.000Z"
        },
        [1] {
                             "id" => 2,
                   "command_name" => "host-notify-by-pager",
                   "command_line" => "/usr/local/bin/host-notify-pager",
            "command_description" => "Host Alert by Pager",
                     "created_at" => "2015-01-19T17:24:33.000Z",
                     "updated_at" => "2015-01-19T17:24:33.000Z"
        }
    ],
    "service_notification_commands" => [
        [0] {
                             "id" => 4,
                   "command_name" => "service-notify-by-email",
                   "command_line" => "/usr/local/bin/service-notify",
            "command_description" => "Service Alert",
                     "created_at" => "2015-01-19T17:24:44.000Z",
                     "updated_at" => "2015-01-19T17:24:44.000Z"
        }
    ]
}

经过strong_parameters之后就变成了这个:

基本上是同一件事,只有created_atupdated_at被剥离和 _attributes附加到属性名称,因此它适用于accept_nested_attributes_for

{
                                "contact_name" => "joe-user",
       "host_notification_commands_attributes" => [
        [0] {
                             "id" => 1,
                   "command_name" => "host-notify-by-email",
                   "command_line" => "/usr/local/bin/host-notify",
            "command_description" => "Host Alert"
        },
        [1] {
                             "id" => 2,
                   "command_name" => "host-notify-by-pager",
                   "command_line" => "/usr/local/bin/host-notify-pager",
            "command_description" => "Host Alert by Pager"
        }
    ],
    "service_notification_commands_attributes" => [
        [0] {
                             "id" => 4,
                   "command_name" => "service-notify-by-email",
                   "command_line" => "/usr/local/bin/service-notify",
            "command_description" => "Service Alert"
        }
    ]
}

编辑#3:我启用了MySQL查询日志记录,但我根本没有看到UPDATE / INSERT个查询。我如何调试为什么contact.update(safe_params)没有做任何事情?

编辑#4:作为一个没有AngularJS或POST的简单测试,我做了一个简单的rake任务,定义了一个JSON对象并尝试更新数据库。我得到了同样的问题,所以我很确信这个问题出现在我的模型中......但是在哪里???

请看一下这个要点https://gist.github.com/pruchai/6afe74b170da2a3d307f

1 个答案:

答案 0 :(得分:2)

构建表单并使用nested_fields_for时,您的表单结构已正确构建。因此,理想情况下,您首先尝试“老式的方式”,检查推送到服务器的内容并模仿它。

所以一般情况下我不是手工完成这个,但是我维护一个gem来处理嵌套表单(cocoon),所以如果我没有弄错,要使accepts_nested_attributes起作用,参数必须如下所示:

{
    "contact_name" => "joe-user",
    "host_notification_commands_attributes" => {
      "0" => {
        "id" => 1,
        "command_name" => "host-notify-by-email",
        "command_line" => "/usr/local/bin/host-notify",
        "command_description" => "Host Alert"
      },
      "1" => {
        "id" => 2,
        "command_name" => "host-notify-by-pager",
        "command_line" => "/usr/local/bin/host-notify-pager",
        "command_description" => "Host Alert by Pager"
      }
    },
    "service_notification_commands_attributes" => {
      "0" => {
        "id" => 4,
        "command_name" => "service-notify-by-email",
        "command_line" => "/usr/local/bin/service-notify",
        "command_description" => "Service Alert"
      }, 
      "12342233444444" => {
        "command_name" => "service-notify-by-email",
        "command_line" => "/usr/local/bin/service-notify",
        "command_description" => "Service Alert"
      }
    }
  }

因此.._attributes应该包含一个哈希,而不是一个数组。关键是(现有)数组中的位置,我asuume。在cocoon中,我用这个虚拟索引填充一些从当前时间派生的大数字。我假设这个顺序应该对应于关系的默认顺序。但是如果你把“id”字段放下来,那么rails也可以使用它。不确定。要销毁现有元素,您必须设置_destroy属性(并在强参数中允许它)。

对于新项目的Imho id应为空,否则rails将假定该项存在(因为通常不会在客户端生成ID)。