Pergunta

Estou usando vários modelos infantis de forma complexa (a la http://railsforum.com/viewtopic.php?id=28447). O formulário funciona muito bem, mas preciso validar uma propriedade do definir de modelos infantis antes de aceitar os dados do formulário no banco de dados. Eu criei uma maneira principalmente de fazer isso. Parece que tem que haver uma maneira melhor, então estou pedindo sugestões ...

Basicamente, uma pessoa possui distribuições. A distribuição tem (entre outras coisas) um atributo percentual. Para uma determinada pessoa, suas distribuições devem totalizar 100% para serem válidas. Isso grita "transação" para mim, mas achei que deveria dar um tiro aos validadores primeiro.

Tentei escrever isso como um validador personalizado, mas o validador funciona apenas em dados já salvos no banco de dados. Ele não verificou os parâmetros enviados pelo formulário. Em outras palavras, pude inserir porcentagens inválidas através do formulário, que foram salvas e, em seguida, as edições subsequentes falharam devido aos dados ruins já no modelo.

Em seguida, estendi update_attributes no meu modelo de pessoa, adicionando uma transação:

def update_attributes(params)
  retval = true
  self.transaction do
    retval = super

    unless distributions_exactly_100?
      retval = false
      errors.add_to_base("Distribution must add up to exactly 100%")
      raise ActiveRecord::Rollback
    end
  end
  retval
end

O negócio retval é feio, mas isso mais ou menos funciona (às vezes algumas das distribuições pendentes estão faltando no formulário, quando encontra um erro e renderizadores). Há uma nuance adicional de que me convence, essa é uma abordagem ruim: se minha associação de distirbions é definida com métodos auxiliares, como abaixo, não posso usar os métodos auxiliares no meu update_attributes() (ou em distributions_exactly_100?), porque eles vão para o banco de dados, em vez de operar no conjunto de distribuições recém-assinadas por não-comprometidas.

 has_many :distributions do
   def for_month_and_year(month, year)
     find :all, :conditions => ['month = ? and year = ?', month, year]
   end

   def total_for_month_and_year(month, year)
     sum :percentage, :conditions => ['month = ? and year = ?', month, year]
   end

   ...

   def years_and_months
     ds = find(:all, :order => 'year DESC, month DESC')
     (ds.collect {|d| [d.year, d.month]}).uniq
   end

 end

A única outra coisa em que consigo pensar é processar os próprios parâmetros, como texto, a caminho do Update_Attributes. Mas isso está errado. :)

Alguém mais fazendo validação em uma coleção inteira de crianças? Qual é o caminho certo para fazer isso?

Foi útil?

Solução

Eu não recomendo definir erros em update_attributes. Mantenha as validações em seu lugar normal.

De volta ao seu problema, você pode alterar a verificação de validação para funcionar nas distribuições na memória, em vez de executar o cálculo no banco de dados?

# in Person model
validate :ensure_distributions_equal_100

def ensure_distributions_equal_100
  percent = distributions.map(&:percent).sum
  if percent != 100
    errors.add_to_base("Distribution must add up to exactly 100%, they are #{percent}")
  end
end
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top