当用户提交一个表单,并留有一定领域的空白,他们得到保存在数据库中的空白。 我想通过PARAMS迭代[:用户]集合(例如),如果字段为空白,更新属性之前将其设置为零。 我无法弄清楚如何做到这一点,虽然我知道迭代的唯一方法创建新的对象:
coll = params[:user].each do |c|
if c == ""
c = nil
end
end
谢谢。
当用户提交一个表单,并留有一定领域的空白,他们得到保存在数据库中的空白。 我想通过PARAMS迭代[:用户]集合(例如),如果字段为空白,更新属性之前将其设置为零。 我无法弄清楚如何做到这一点,虽然我知道迭代的唯一方法创建新的对象:
coll = params[:user].each do |c|
if c == ""
c = nil
end
end
谢谢。
考虑你正在使用的过滤器在控制器中保存的影响或更新时模型的行为在这里做什么。 我认为一个更清洁的方法将是一个before_save
回调模型或观察员。 这样,你得到相同的行为无论在哪里变化经由控制器,控制台甚至运行批处理过程时,无论其起源。
例:
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
这将产生预期的行为:
>> 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
>>
如果你只是想杀人的空白,你可以做params.delete_if {|k,v| v.blank?}
params.delete_if {|k,v| v.blank?}
。
一个良好的创业板在模型中处理此: https://github.com/rmm5t/strip_attributes
它定义了一个before_validation
挂钩,修剪空格,并设置空字符串为零。
before_save似乎是错误的位置给我,如果你想在保存之前使用的值是什么。 所以我推翻的制定者,而不是:
# 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
只要是完整的,我把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
并使用它像这样:
class MyModel
include MyModelExtensions
nil_if_blank :attr1, :attr2
end
通常我会鼓励的功能被移动到模型中,在其他的答案说,这意味着你会得到相同的行为无论身在何处的变化从起源。
不过,我不认为在这种情况下,它是正确的。 该影响被发现纯属下降到不能够编码在HTTP请求一个空字符串和零值之间的差异。 出于这个原因,它应该在控制器级别进行补救。 这也意味着,在其他地方,仍然可以存储在模型中一个空字符串(其中有可能是有正当的理由,如果没有它简单与标准的检验覆盖)。
我使用的是解决这个问题的代码是:
# 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
...
我试图保持代码尽可能简洁,虽然可读性有所遭遇,所以这里是一个测试用例来演示其功能:
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
这然后可以在像这样的控制器可以使用:
...
@book.update_attributes(clean_params[:book])
...
您可以使用attribute_normalizer宝石和使用空白正规化,将在零值变换空字符串。
你可以做到这一点使用注射,这是显而易见的,以正在发生的事情。
params = params.inject({}){|new_params, kv|
new_params[kv[0]] = kv[1].blank? ? nil : kv[1]
new_params
}
还有一个黑客可以通过与自身融合,并传递一个块来处理新的价值与合并做(虽然这不是真正为它的用途,但它更简洁)
params.merge(params){|k, v| v.blank? ? nil : v}
使用“到位”收集方法(也被称为地图!)
params[:user].collect! {|c| c == "" ? nil : c}
克里斯,
这里是有相思值则params的递归解析。
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
在ApplicationController中:
class ApplicationController < ActionController::Base
def nilify(p)
p.transform_values!{|v| v.present? ? v : nil }
end
end
在你的控制器中,修改参数强滤波方法调用nilify:
class UserController < ApplicationController
def user_params
nilify params.require(:user).permit(:email, :name)
end
end
我广义的答案和由钩/延伸,可以用来作为一个初始值设定。 这使得它可以在多个机型上使用。 我已经添加了它作为我的一部分ActiveRecordHelpers GitHub上回购
这里是我做到了。
def remove_empty_params(param, key)
param[key] = param[key].reject { |c| c.empty? }
end
并把它与
remove_empty_params(params[:shipments], :included_clients)
没有必要让模型中的超级棘手。 而这种方式,您可以控制哪些PARAMS得到清理。
params = {
"shipments"=>{
"included_clients" => ["", "4"]
}
}
会变成
>> params["shipments"]
=> {"included_clients" => ["4"] }
如果你知道你要编码空白视为尼尔斯你可以使用下面的属性二传手覆盖哪些属性:
def colour=(colour)
super(colour.blank? ? nil : colour)
end
有点笨重,如果你有很多的属性来覆盖,虽然。