Reputation: 301
I'm having trouble with Active Record callbacks in a model which contains accepts_nested_attributes
I call build_associated_parties
using an after_create
callback, but these values are not being saved and I get <nil>
errors. I've also tried using before_create
& after_initialize
callbacks without success.
What is causing the callbacks to fail?
connection.rb
class Connection < ActiveRecord::Base
attr_accessible :reason, :established, :connector, :connectee1,
:connectee2, :connectee1_attributes,
:connectee2_attributes, :connector_attributes
belongs_to :connector, class_name: "User"
belongs_to :connectee1, class_name: "User"
belongs_to :connectee2, class_name: "User"
accepts_nested_attributes_for :connector, :connectee1, :connectee2
belongs_to :permission
after_create :build_associated_parties
# builds connectee's, connector, permission objects
def build_associated_parties
build_connector
build_connectee1
build_connectee2
build_permission
end
connection_controller.rb
class ConnectionsController < ApplicationController
def new
@connection = Connection.new
end
def create
@connection = Connection.new params[:connection]
if @connection.save
flash[:notice] = "Connection created successfully!"
redirect_to @connection
else
render :new
end
end
end
However, if I instead build these attributes inside the controller as shown below, I don't get the error. This is nice, but it seems to go against keeping business logic code out of the controller.
class ConnectionsController < ApplicationController
def new
@connection = Connection.new
@connection.build_connectee1
@connection.build_connectee2
@connection.build_connector
end
end
How can I accomplish the same functionality with code in the model? Are there advantages to keeping it in the model?
Upvotes: 0
Views: 455
Reputation: 301
Moved logic out of controller and back into model. However, the build_*
code was overwriting the values I was passing into the nested attributes.
By adding unless {attribute} to these build_
methods, I was able to properly pass in the values.
class Connection < ActiveRecord::Base attr_accessible :reason, :established, :connector, :connectee1, :connectee2, :connectee1_attributes, :connectee2_attributes, :connector_attributes
belongs_to :connector, class_name: "User"
belongs_to :connectee1, class_name: "User"
belongs_to :connectee2, class_name: "User"
accepts_nested_attributes_for :connector, :connectee1, :connectee2
belongs_to :permission
after_initialize :build_associated_parties
validates :reason, :presence => true
validates_length_of :reason, :maximum => 160
#builds connectee's, connector, permission objects
def build_associated_parties
build_connector unless connector
build_connectee1 unless connectee1
build_connectee2 unless connectee2
end
Upvotes: 0
Reputation: 1976
after_create
is a big no. Use after_initialize
in your model and use self
inside your build_associated_parties
method. See if that works.
Upvotes: -1
Reputation: 8604
You called your method build_associated_parties
after connection
is created, so how these methods:
build_connector
build_connectee1
build_connectee2
build_permission
know what params it will use? So they don't know what values are passed into method then they will get error. In controller, they didn't have error because they used values of params[:connection]
.
On your form, if you already have fields for connector, connectee1, connectee2
, you should put code which initialize object in your new controller. When you save @connection
, it's saved those object too. I think these codes aren't need to put into model. Your model only should put other logic code, like search or calculation...
Upvotes: 1