问题描述:

I'm having trouble with Active Record callbacks in a model which contains accepts_nested_attributes

I call build_associated_partiesusing 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?

网友答案:

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...

网友答案:

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.

网友答案:

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
相关阅读:
Top