-
-
Save pruchai/6afe74b170da2a3d307f to your computer and use it in GitHub Desktop.
namespace :post do | |
desc "Test Post" | |
task :nested_test => :environment do | |
def contact_params | |
params = { | |
:id => '1', | |
:contact_name => 'joe-user', | |
:host_notification_commands_attributes => { | |
'0' => {"id"=>1, "command_name"=>"command1", "command_line"=>"/usr/local/bin/command1", "command_description"=>"Command One"}, | |
'1' => {"id"=>2, "command_name"=>"command2", "command_line"=>"/usr/local/bin/command2", "command_description"=>"Command Two"} | |
}, | |
:service_notification_commands_attributes => { | |
'0' => {"id"=>3, "command_name"=>"command3", "command_line"=>"/usr/local/bin/command3", "command_description"=>"Command Three"}, | |
'1' => {"id"=>4, "command_name"=>"command4", "command_line"=>"/usr/local/bin/command4", "command_description"=>"Command Four"} | |
} | |
return params | |
end | |
begin | |
contact = Contact.find_by_id(contact_params[:id]) | |
contact.update(contact_params) | |
rescue => error | |
puts error | |
end | |
end | |
end |
# | |
# Both, contact and command exist in the database | |
# | |
% rails c | |
Loading development environment (Rails 4.2.0) | |
irb(main):001:0> contact = Contact.find_by_id(1) | |
Contact Load (0.2ms) SELECT `contacts`.* FROM `contacts` WHERE `contacts`.`id` = 1 LIMIT 1 | |
=> #<Contact id: 1, contact_name: "pruchai", created_at: "2015-01-19 22:44:10", updated_at: "2015-01-20 07:45:15"> | |
irb(main):002:0> command = Command.find_by_id(1) | |
Command Load (0.2ms) SELECT `commands`.* FROM `commands` WHERE `commands`.`id` = 1 LIMIT 1 | |
=> #<Command id: 1, command_name: "command1", command_line: "/usr/local/bin/command1", command_description: "Command One", created_at: "2015-01-19 17:24:12", updated_at: "2015-01-21 03:29:03"> | |
irb(main):003:0> |
# | |
# Running the task to simply eastablish relationship between 2 existing objects | |
# | |
# WHY IS IT TRYING TO FIND IT, INSTEAD OF CREATING IT??? | |
# | |
% rake post:nested_test | |
Couldn't find Command with ID=1 for Contact with ID=1 |
@nathanvda: Really appreciate you taking your time to help. Thank you!
That is correct. At this point I am working with existing records and just trying to establish relationships between them. I have a contact
with ID=1 and I have a command
with ID=1, but they are no related to each other in any way shape of form. They are separate entities that do not know about each other.
My form has 2 drop down menus. They are identical and are populated with a list of commands that I have in commands table.
commands = Command.all
The only difference between them is when i select a command in one drop down, a command
object gets pushed into contact.host_notification_commands
and another one pushes objects into contact.service_notification_commands
. A contact
may be associated with 0 or more notification commands of either type. Same command
can be a host and a service notification command at the same time.
So, lets say that I want to update a contact with ID=1, which is not currently associated with any host or service commands, so I select a contact like this:
def show
contact = Contact.find_by_id(params[:id])
render json: contact[0].to_json(:include => [ :host_notification_commands, :service_notification_commands ] )
end
This results in the following contact
object loaded into the form:
{
:id => 1,
:contact_name => 'joe-user',
:host_notification_commands => [],
:service_notification_commands => []
}
After that, I select an existing command named command1 from a drop down, which turns my contact
objects into this:
{
:id => 1,
:contact_name => 'joe-user',
:host_notification_commands => [
{
:id => 1,
:command_name => 'command1',
:command_line => '/usr/local/bin/command1',
:command_description => 'Command One'
# :created_at and :updated_at also included, but ill leave them out for now
}
],
:service_notification_commands => []
}
Now I do POST to my controller. POST parameters go trough strong_parameters
and then to my update
function.
def update
contact = Contact.find_by_id(params[:id])
contact.update(contact_params)
end
And this is where it bombs. I really have no idea if I am mis-interpreting what Rails is supposed to do. I am under the assumption that it is supposed to take my contact
and establish relationships with the command
via commands_contacts
table and set :notification_type
column to host
.
Instead, Rails runs a SELECT
query to see if my contact with ID=1 has a relationship with a command
with ID=1. Obviously, it fails, because this relationship is not there and I am just now trying CREATE it.
I created a couple of self-contained scripts as a test:
This one just tests the model and passes all the tests
https://gist.github.com/pruchai/c2e190611aa02c5a03b9
This one is pretty much the same, but adds a controller and tries to run an update on contact
https://gist.github.com/pruchai/0f8e134c1733e5cd40b6
The fact that I am able to reproduce the same behavior with the same error in 3 different ways tells me that I am either misunderstanding Rails functionality (very likely), hitting some kind of bug (I doubt that) or have problems with my logic (most probable scenario)
Ah I see now! You are trying to link existing commands with contacts, right? In that case you should not be filling the host_notification_commands
, but you should be filling the join-table command_contacts
.
Correct. But filling host_notification_commands
and service_notification_commands
does, indeed, fill the command_contacts
table.
host_notification_commands
and service_notification_commands
are "virtual" associations setup in my Contact
model.
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 :host_notification_commands
accepts_nested_attributes_for :service_notification_commands
end
So, essentially, they are the same as Contact
<--> Command
association, but they automatically set the notification_type
column in command_contacts
table.
Regular has_many through Contact
<--> Command
relationship knows nothing about the notification_type
, so i don't even know how I would set that field, without using additional host_notification_commands
and service_notification_commands
relationships
has_many :commands, :through => :commands_contacts
Here is a small demonstration
contact = Contact.create!
# This does not meet my needs, because it does not set notification_type
contact.commands << Command.create!
# These 2 do meet my needs, because they set notification_type
contact.host_notification_commands << Command.create!
contact.service_notification_commands << Command.create!
commands_contacts = CommandsContact.all
commands_contacts.each do |cc|
puts 'Contact ID: ' + cc.contact_id.to_s + ' / Command ID: ' + cc.command_id.to_s + ' / Notification Type: ' + cc.notification_type.to_s
end
Running above code, results in following output:
Contact ID: 1 / Command ID: 1 / Notification Type:
Contact ID: 1 / Command ID: 2 / Notification Type: host
Contact ID: 1 / Command ID: 3 / Notification Type: service
As you can see, everything goes into the commands_contacts
as expected. The only difference is the notification_type
column
Let me get this straight:
Contact
with ID=1 existsCommand
with ID=1 exists,but you do not show the
CommandContacts
table, is it of the right type? You give theid
of the commands, so it will go looking for existing commands. While a command with ID=1 exists, it maybe is not a host command? (notification_type
= :host).You should show the output of
contact.host_notification_commands
.If you are creating new items, the ids should be empty.