to_h strong rails permit ruby-on-rails ruby forms collections

ruby-on-rails - rails - strong parameters nested attributes



Hacer params en blanco[] nil (12)

Así es como lo hice.

def remove_empty_params(param, key) param[key] = param[key].reject { |c| c.empty? } end

y llámalo con

remove_empty_params(params[:shipments], :included_clients)

No hay necesidad de ser súper complicado en el modelo. Y de esta manera puedes controlar qué parámetros se limpian.

params = { "shipments"=>{ "included_clients" => ["", "4"] } }

se convertirá en

>> params["shipments"] => {"included_clients" => ["4"] }

Cuando un usuario envía un formulario y deja ciertos campos en blanco, se guardan como en blanco en la base de datos. Me gustaría recorrer la colección params [: user] (por ejemplo) y si un campo está en blanco, configúrelo en nil antes de actualizar los atributos. No puedo entender cómo hacerlo, ya que la única forma que conozco para iterar crea nuevos objetos:

coll = params[:user].each do |c| if c == "" c = nil end end

Gracias.


Chris

Aquí hay un análisis recursivo de parámetros que tienen valores blancos.

before_filter :process_params ...... private def process_params .... set_blanc_values_to_nil(params) end # Maybe move method to ApplicationController # recursively sets all blanc values to nil def set_blanc_values_to_nil!(my_hash) my_hash.keys.each do |key| val = my_hash[key] next if val.nil? my_hash[key] = nil if val.is_a?(String) && val.empty? set_blanc_values_to_nil!(val) if val.is_a? Hash end end


Considere lo que está haciendo aquí usando filtros en el controlador para afectar el comportamiento de un modelo cuando se guarda o actualiza. Creo que un método mucho más limpio sería una llamada before_save en el modelo o un observador. De esta manera, obtendrá el mismo comportamiento sin importar de dónde se origine el cambio, ya sea a través de un controlador, la consola o incluso cuando se ejecutan procesos por lotes.

Ejemplo:

class Customer < ActiveRecord::Base NULL_ATTRS = %w( middle_name ) before_save :nil_if_blank protected def nil_if_blank NULL_ATTRS.each { |attr| self[attr] = nil if self[attr].blank? } end end

Esto produce el comportamiento esperado:

>> c = Customer.new => #<Customer id: nil, first_name: nil, middle_name: nil, last_name: nil> >> c.first_name = "Matt" => "Matt" >> c.middle_name = "" # blank string here => "" >> c.last_name = "Haley" => "Haley" >> c.save => true >> c.middle_name.nil? => true >>


Generalicé una respuesta e hice un gancho / extensión que se puede usar como inicializador. Esto permite que se utilice en múltiples modelos. Lo he agregado como parte de mi repo de ActiveRecordHelpers en GitHub


Podría hacer esto usando inyectar, lo que es obvio en cuanto a lo que está sucediendo.

params = params.inject({}){|new_params, kv| new_params[kv[0]] = kv[1].blank? ? nil : kv[1] new_params }

También hay un truco que puede hacer con la fusión fusionándose consigo mismo y pasando un bloque para manejar el nuevo valor (aunque este no es realmente el uso que se pretende, pero es más conciso)

params.merge(params){|k, v| v.blank? ? nil : v}


Por lo general, recomendaría que la funcionalidad se mueva al modelo, como se indica en otras respuestas, esto significa que obtendrá el mismo comportamiento sin importar de dónde se origine el cambio.

Sin embargo, no creo que en este caso sea correcto. El efecto que se está notando es simplemente no poder codificar la diferencia entre una cadena en blanco y un valor nulo en la solicitud HTTP. Por esta razón, debe remediarse a nivel de controlador. También significa que en otros lugares todavía es posible almacenar una cadena vacía en el modelo (lo que podría haber por una razón legítima, y ​​si no es así, es fácil de cubrir con validaciones estándar).

El código que estoy usando para superar este problema es:

# application_controller.rb ... def clean_params @clean_params ||= HashWithIndifferentAccess.new.merge blank_to_nil( params ) end def blank_to_nil(hash) hash.inject({}){|h,(k,v)| h.merge( k => case v when Hash : blank_to_nil v when Array : v.map{|e| e.is_a?( Hash ) ? blank_to_nil(e) : e} else v == "" ? nil : v end ) } end ...

Traté de mantener el código lo más conciso posible, aunque la legibilidad ha sufrido un poco, así que aquí hay un caso de prueba para demostrar su funcionalidad:

require "test/unit" class BlankToNilTest < Test::Unit::TestCase def blank_to_nil(hash) hash.inject({}){|h,(k,v)| h.merge( k => case v when Hash : blank_to_nil v when Array : v.map{|e| e.is_a?( Hash ) ? blank_to_nil(e) : e} else v == "" ? nil : v end ) } end def test_should_convert_blanks_to_nil hash = {:a => nil, :b => "b", :c => ""} assert_equal( {:a => nil, :b => "b", :c => nil}, blank_to_nil(hash) ) end def test_should_leave_empty_hashes_intact hash = {:a => nil, :b => "b", :c => {}} assert_equal( {:a => nil, :b => "b", :c => {}}, blank_to_nil(hash) ) end def test_should_leave_empty_arrays_intact hash = {:a => nil, :b => "b", :c => []} assert_equal( {:a => nil, :b => "b", :c => []}, blank_to_nil(hash) ) end def test_should_convert_nested_hashes hash = {:a => nil, :b => "b", :c => {:d => 2, :e => {:f => "", :g => "", :h => 5}, :i => "bar"}} assert_equal( {:a => nil, :b => "b", :c => {:d => 2, :e => {:f => nil, :g => nil, :h => 5}, :i => "bar"}}, blank_to_nil(hash) ) end def test_should_convert_nested_hashes_in_arrays hash = {:book_attributes => [{:name => "b", :isbn => "" },{:name => "c", :isbn => "" }], :shelf_id => 2} assert_equal( {:book_attributes => [{:name => "b", :isbn => nil},{:name => "c", :isbn => nil}], :shelf_id => 2}, blank_to_nil(hash)) end def test_should_leave_arrays_not_containing_hashes_intact hash = {:as => ["", nil, "foobar"]} assert_equal( {:as => ["", nil, "foobar"]}, blank_to_nil(hash)) end def test_should_work_with_mad_combination_of_arrays_and_hashes hash = {:as => ["", nil, "foobar", {:b => "b", :c => "", :d => nil, :e => [1,2,3,{:a => "" }]}]} assert_equal( {:as => ["", nil, "foobar", {:b => "b", :c => nil, :d => nil, :e => [1,2,3,{:a => nil}]}]}, blank_to_nil(hash)) end end

Esto se puede usar en un controlador así:

... @book.update_attributes(clean_params[:book]) ...


Puede usar la gema attribute_normalizer y el normalizador en blanco que transformará las cadenas vacías en valores nulos.


Si sabe para qué atributos desea codificar espacios en blanco como nils, puede usar la siguiente anulación del definidor de atributos:

def colour=(colour) super(colour.blank? ? nil : colour) end

Un poco voluminoso si tienes muchos atributos que cubrir.


Si solo quieres eliminar los espacios en blanco, puedes hacer params.delete_if {|k,v| v.blank?} params.delete_if {|k,v| v.blank?} .



Utilice el método de recopilación "en el lugar" (también conocido como mapa)

params[:user].collect! {|c| c == "" ? nil : c}


before_save me parece la ubicación incorrecta, ¿qué ocurre si desea utilizar el valor antes de guardar? Así que anulé a los setters en su lugar:

# include through module or define under active_record def self.nil_if_blank(*args) args.each do |att| define_method att.to_s + ''='' do |val| val = nil if val.respond_to?(:empty?) && val.empty? super(val) end end end #inside model nil_if_blank :attr1, :attr2

Solo para completar, pongo lo siguiente en lib / my_model_extensions.rb

module MyModelExtensions def self.included(base) base.class_eval do def self.nil_if_blank(*args) args.each do |att| define_method att.to_s + ''='' do |val| val = nil if val.respond_to?(:empty?) && val.empty? super(val) end end end end end end

y úsalo así:

class MyModel include MyModelExtensions nil_if_blank :attr1, :attr2 end