2

I've a Register model which has_many :telephones Register model accepts_nested_attributes_for :telephones, :reject_if number and code blank?, and has attr_accessible :telephones_attributes (and all other fields)

Telephones belongs_to :register and has attr_accessible for all fields

When the form sends the params, it seems to be well formed, but when the form fields are sent to @register.attributes = params[:register] it will update all fields but not the existing telephones(which should have gotten an update to its fields as well), they just stay the way they were before(I checked when debugging)

Now this is where it gets weird, in the console when I test it, I was thinking it was something about params, because I could make it work there, now I discovered it behaves like I want for the has_many association only after the first time I try to assign it, I tried this in controller(mass assigning params two times) and it works.

Of course it must have something to do with the fact that I'm using model.attributes = params to assign, Im doing it this way so I can also save a monitoring of the changes to the system(and I'm not doing this in before_filters because I have to access some data I feel is specific to the controller like current_user and on) maybe is poor design... but isn't it weird?I have to mass-assign two times for this to work? Is this some kind of bug? Does anyone know if there's a way to fix it?

  • I'm on rails 2.3.8, ruby 1.8.6

Edit: Here's sample code, a lot of this code here is in portuguese, tho :/

telefone = telephones, cadastro = register

As you can see I've got an all_changes method that aggregates changes done to some associations, theres a custom setter for comments(comentario_interno/externo) as means to adding one comment at time...

    #models

class Telefone < ActiveRecord::Base
  #relações
  belongs_to :cadastro
  #validações
  validates_presence_of :ddd
  validates_presence_of :numero
  validates_numericality_of :ddd
  validates_numericality_of :numero

  attr_accessible :ddd, :numero, :cadastro_id, :id

end

class Cadastro < ActiveRecord::Base
  #relações
  #cliente
  belongs_to :estado
  belongs_to :indicacao
  has_many :telefones
  has_one :outra_indicacao
  #venda
  belongs_to :user
  belongs_to :banco
  belongs_to :plano
  belongs_to :pacote
  belongs_to :situacao
  belongs_to :situacao_diversa
  has_many :comentario_internos
  has_many :comentario_externos

  #system
  #has_many :sys_logs
  has_many :sys_logs, :as => :monitorable
  has_many :email_history, :through => :sys_logs, :conditions => {:type => 'SysEmail'} , :source => :sys_actions
  has_many :lock_history, :through => :sys_logs, :conditions => {:type => 'SysLock'}, :source => :sys_actions
  has_many :alteracao_history, :through => :sys_logs, :conditions => {:type => 'SysAlteracao'}, :source => :sys_actions

  #filtros

  #validações
  #cliente
  validates_presence_of :tipo, :nome, :cpfcnpj, :rg, :data_nascimento, :profissao, :filiacao, :email, :logradouro,
                        :tp_logradouro, :numero, :bairro, :cep, :cidade
  validates_uniqueness_of :cpfcnpj
  validates_presence_of :estado
  #validate :must_have_at_least_one_telephone
  #venda
  validates_presence_of :user
  validates_presence_of :situacao
  validates_numericality_of :agencia, :allow_blank => true
  validates_numericality_of :digito_agencia, :allow_blank => true
  validates_numericality_of :cc, :allow_blank => true
  validates_numericality_of :digito_cc, :allow_blank => true
  validates_numericality_of :cpf_titular, :allow_blank => true
  #cpf must be unique




  accepts_nested_attributes_for :telefones, :reject_if => lambda {|attr| attr['ddd'].blank? && attr['numero'].blank?}
  accepts_nested_attributes_for :outra_indicacao
  accepts_nested_attributes_for :comentario_internos, :reject_if => lambda {|attr| attr['comentario'].blank?}
  accepts_nested_attributes_for :comentario_externos, :reject_if => lambda {|attr| attr['comentario'].blank?}

  #attr_accessible :new_comentario_interno, :new_comentario_externo, :telefones_attributes

  attr_accessible :telefones_attributes, :new_comentario_interno, :new_comentario_externo, :outra_indicacao_attributes,
                  :user_id, :cc, :digito_cc, :data_instalacao, :cpfcnpj, :profissao, :tp_logradouro, :agencia, :cpf_titular,
                  :situacao_id, :estado_id, :plano_id, :banco_id, :nome, :data_nascimento, :cep, :observacao, :data_agendamento,
                  :dia_vencimento, :digito_agencia, :pacote_id, :nome_titular, :logradouro,
                  :indicacao_id, :telefones_attributes, :contrato, :confirmacao_condicoes, :estado_civil, :cidade,
                  :horario_retorno, :tipo, :sexo, :filiacao, :complemento, :bairro, :rg, :expeditor, :email, :numero,
                  :situacao_diversa_id

  def new_comentario_interno=(attributes = {})
    self.comentario_internos << ComentarioInterno.new(:user_id => attributes[:user_id], :comentario => attributes[:comentario]) unless attributes[:comentario].blank?
  end

  def new_comentario_externo=(attributes = {})
    self.comentario_externos << ComentarioExterno.new(:user_id => attributes[:user_id], :comentario => attributes[:comentario]) unless attributes[:comentario].blank?
  end

  def self.buscar_cadastros(options = {})
    conditions = []
    conditions << sanitize_sql(["cadastros.situacao_id = ?", options[:situacao_id]]) unless options[:situacao_id].blank?
    conditions << sanitize_sql(["cadastros.user_id = ?", options[:user_id]]) unless options[:user_id].blank?
    conditions << sanitize_sql(["cadastros.created_at >= ? AND cadastros.created_at < ?",
                                Date.civil(options[:ano].to_i, options[:mes].to_i, 1),
                                Date.civil(options[:ano].to_i, options[:mes].to_i, -1)]) unless options[:ano].blank? || options[:mes].blank?

    self.find(:all, :conditions => conditions.join(" AND "))
  end

  def self.vendas_count_on(situacao_id, options = {})
    select = sanitize_sql(["SELECT count(*) FROM cadastros LEFT JOIN situacaos ON cadastros.situacao_id = situacaos.id
    WHERE situacaos.id = ?", situacao_id])
    select << sanitize_sql([" AND cadastros.user_id = ?", options[:user_id]]) unless options[:user_id].blank?
    select << sanitize_sql([" AND cadastros.created_at >= ? AND cadastros.created_at < ?",
                            Date.civil(options[:ano].to_i, options[:mes].to_i, 1),
                            Date.civil(options[:ano].to_i, options[:mes].to_i, -1)]) unless options[:ano].blank? || options[:mes].blank?

    count_by_sql(select)
  end

  def all_changes
    #agregar telefones, outra indicacao, comentarios internos, comentarios externos
    changes = self.changes
    h = Hash.new
    h["outra_indicacao"] = self.outra_indicacao.descricao_change if self.outra_indicacao && self.outra_indicacao.changed?

    if self.id
      old_telefones = connection.execute("select ddd || '-' || numero as numformat from telefones where cadastro_id = #{self.id}").collect {|t| t["numformat"]}
    else
      old_telefones = []
    end
    new_telefones = self.telefones.collect {|t| "#{t.ddd}-#{t.numero}"}
    h["telefones"] = [old_telefones.join(', '), new_telefones.join(', ')] unless (old_telefones - new_telefones).empty?
    changes.delete("syslogid")
    changes.merge(h)
  end

  def locked?
    #pegar o ultimo lock e retornar o valor
    last_lock = self.lock_history.last
    if last_lock
      return last_lock.locked?
    else
      return false
    end
  end

end

#here's what Ive got to do in controller for this to work, remember its only when updating existing phones, creating is working normally

@cadastro.attributes = params[:cadastro]
    @cadastro.attributes = {:telefones_attributes => params[:cadastro][:telefones_attributes]}
  • Can you provide example code? Your method of setting attributes with model.attributes = params should work. My guess is that you are slipping up somewhere else with an invalid or missing attribute. – Pan Thomakos Dec 16 '10 at 23:52

1 Answers1

0

It's not clear in your example how all_changes is used or if it is even called. Try simplifying your code to see if the issue is actually with Rails or with your implementation. For example, the following should work:

class Telefone < ActiveRecord::Base
  belongs_to :cadastro
  validates_presence_of :ddd
  validates_presence_of :numero
  validates_numericality_of :ddd
  validates_numericality_of :numero
end

class Cadastro < ActiveRecord::Base
  has_many :telefones

  accepts_nested_attributes_for :telefones
end

@cadastro.attributes = {:telefones_attributes => [{:ddd => 111, :numero => 1234567}]}
@cadastro.save

You should also notice that you don't need the reject_if condition in your accepts_nested_attributes_for because you are already validating the presence of :ddd and :numero in the base class.

Pan Thomakos
  • 34,082
  • 9
  • 88
  • 85