I can't figure out why my database records are not getting updated, or new records created, for that matter, when I POST from a form.
I am able to manually populate the database and create relationships:
contact = Contact.first
command = Command.find(3)
contact.host_notification_commands << command
And I am also able to load this data info my form. What I can't figure out is how to update or add new records. Everything i've tried so far has failed.
I have 3 models - Command
, Contact
and joined CommandsContact
. commands_contacts
join table is holing an extra attribute :notification_type
, which could either be set to host or service , and my Contact
model has 2 extra relationships setup to i can access :host_notification_commands
and :service_notification_commands
. This gives me an ability to do things like Contact.fist.host_notification_commands
or Contact.find(3).service_notification_commands.
It appears that no UPDATE
or INSERT
queries are ever firing up from the controller, when I to proper POST and I can't figure out how to debug that.
Models
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
And after this everything falls apart.
Controller
Due to the fact that I am using accepts_nested_attributes_for
, I have to append _attributes
to the names of my nested objects - :host_notification_commands
and :service_notification_commands
. I'll change my form to submit it that way, but simple re-assignment works for an example sake.
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
Updating existing record results in:
#<ActiveRecord::RecordNotFound: Couldn't find Command with ID=2 for Contact with ID=1>
Of course it does not exist! I am trying to create this relationship!
Adding a new one, I get:
#<ActiveRecord::RecordNotFound: Couldn't find Command with ID=1 for Contact with ID=>
Absolutely correct. The user haven't even been created yet and relationship is not established with the commands, why is Rails trying to find it???
I am also not seeing any update or insert queries being logged in the rails console, so i guess its not even getting to that point...
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)
What am i missing over here?
EDIT: I guess i could abandon the idea of using strong_parameters
, parse all POST params and then manually populate the database, but thats not very Rails-y.
EDIT #2: Including params posted to the controller.
Data coming in as params[:contact]
from the form
{
"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"
}
]
}
After going through strong_parameters
it becomes this:
Essentially the same thing, only with created_at
and updated_at
stripped and _attributes
appended to the attribute names, so it works with 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"
}
]
}
EDIT #3: I have enabled MySQL query logging but I do not see UPDATE
/ INSERT
queries executed at all. How can i debug why contact.update(safe_params)
is not doing anything?
EDIT #4: As a simple test without AngularJS or POST being in the equation, I made a simple rake tasks that defines a JSON object and tries to update the db. I am getting the same issue, so Im pretty convinced the issue is somewhere in my models... but where???
Please take a look at this Gist https://gist.github.com/pruchai/6afe74b170da2a3d307f
When building a form and using nested_fields_for
your form-structure is built correctly. So ideally you would try first do it the "old fashioned way", check what is pushed to the server and mimick that.
So generally I do not do this by hand, but I maintain a gem to handle nested forms (cocoon), so if I am not mistaken, for the accepts_nested_attributes
to work, the parameters have to look as follows:
{
"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"
}
}
}
So instead of an array the .._attributes
should contain a hash. The key is just the position in the (existing) array, I asuume. In cocoon I fill this dummy index with some large number derived from the current time. I assume this order should correspond to the default order for the relation. But if you hand down the "id" field, it is also possible rails uses that. Not sure. To destroy an existing element, you have to set the _destroy
attribute (and allow it in the strong parameters as well).
Imho for new items the id
should be empty, otherwise rails will assume the item exists (as generally ids are not generated on the client side).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.