Ruby-on-rails 使空白参数[] nil
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1183506/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Make blank params[] nil
提问by chrishomer
When a user submits a form and leaves certain fields blank, they get saved as blank in the DB. I would like to iterate through the params[:user] collection (for example) and if a field is blank, set it to nil before updating attributes. I can't figure out how to do this though as the only way I know to iterate creates new objects:
当用户提交表单并将某些字段留空时,它们会在数据库中保存为空白。我想遍历 params[:user] 集合(例如),如果字段为空,则在更新属性之前将其设置为 nil。我无法弄清楚如何做到这一点,因为我知道迭代创建新对象的唯一方法:
coll = params[:user].each do |c|
if c == ""
c = nil
end
end
Thanks.
谢谢。
采纳答案by Matt Haley
Consider what you're doing here by using filters in the controller to affect how a model behaves when saved or updated. I think a much cleaner method would be a before_savecall back in the model or an observer. This way, you're getting the same behavior no matter where the change originates from, whether its via a controller, the console or even when running batch processes.
通过在控制器中使用过滤器来影响模型在保存或更新时的行为方式,请考虑您在此处所做的事情。我认为更简洁的方法是before_save在模型或观察者中进行回调。这样,无论更改源自何处,无论是通过控制器、控制台还是在运行批处理时,您都会获得相同的行为。
Example:
例子:
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
This yields the expected behavior:
这产生了预期的行为:
>> 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
>>
回答by Chuck
If you just want to kill the blanks, you can just do params.delete_if {|k,v| v.blank?}.
如果你只是想杀死空白,你可以做params.delete_if {|k,v| v.blank?}.
回答by Chuck
A good gem for handling this in the model: https://github.com/rmm5t/strip_attributes
在模型中处理这个问题的好宝石:https: //github.com/rmm5t/strip_attributes
It defines a before_validationhook that trims whitespaces and sets empty strings to nil.
它定义了一个before_validation修剪空格并将空字符串设置为 nil的钩子。
回答by Hampei
before_save seems like the wrong location to me, what if you want to use the value before saving. So I overrode the setters instead:
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
Just to be complete I put the following in lib/my_model_extensions.rb
为了完整起见,我将以下内容放入 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
and use it like this:
并像这样使用它:
class MyModel
include MyModelExtensions
nil_if_blank :attr1, :attr2
end
回答by Bill Lipa
In the ApplicationController:
在应用程序控制器中:
class ApplicationController < ActionController::Base
def nilify(p)
p.transform_values!{|v| v.present? ? v : nil }
end
end
In your controller, modify the strong parameters filter method to call nilify:
在您的控制器中,修改强参数过滤器方法以调用 nilify:
class UserController < ApplicationController
def user_params
nilify params.require(:user).permit(:email, :name)
end
end
回答by pedrosfdcarneiro
You can use attribute_normalizergem and use the blanknormalizer that will transform empty strings in nil values.
您可以使用attribute_normalizergem 并使用将空字符串转换为 nil 值的空白规范器。
回答by Theozaurus
Ordinarily I would encourage functionality to be moved into the model, as stated in other answers this means that you will get the same behavior no matter where the change originates from.
通常,我会鼓励将功能移到模型中,正如其他答案中所述,这意味着无论更改源自何处,您都将获得相同的行为。
However, I don't think in this case it is correct. The affect being noticed is purely down to not being able to encode the difference between a blank string and nil value in the HTTP request. For this reason it should be remedied at the controller level. It also means that in other places it is still possible to store an empty string in the model (which there could be for a legitimate reason for, and if not it is simple to cover with standard validations).
但是,我不认为在这种情况下它是正确的。被注意到的影响纯粹是因为无法对 HTTP 请求中的空白字符串和 nil 值之间的差异进行编码。出于这个原因,它应该在控制器级别进行补救。这也意味着在其他地方,仍然可以在模型中存储一个空字符串(这可能是出于合法原因,如果不是,则很容易用标准验证来覆盖)。
The code I'm using to overcome this problem is:
我用来克服这个问题的代码是:
# 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
...
I've tried to keep the code as concise as possible, although readability has suffered somewhat, so here is a test case to demonstrate its functionality:
我尽量保持代码简洁,尽管可读性有所下降,所以这里有一个测试用例来演示它的功能:
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
This can then be used in a controller like so:
然后可以在控制器中使用它,如下所示:
...
@book.update_attributes(clean_params[:book])
...
回答by glenn Hymanman
Use the "in place" collect method (also known as map!)
使用“就地”收集方法(也称为地图!)
params[:user].collect! {|c| c == "" ? nil : c}
回答by Vlad Zloteanu
Chris,
克里斯,
Here is a recursive parsing of params that have blanc values.
这是具有 blanc 值的参数的递归解析。
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
回答by madlep
You could do this using inject, which is obvious as to what is happening.
您可以使用注入来做到这一点,这对于正在发生的事情是显而易见的。
params = params.inject({}){|new_params, kv|
new_params[kv[0]] = kv[1].blank? ? nil : kv[1]
new_params
}
There is also a hack you can do with merge by merging with itself, and passing a block to handle the new value (although this isn't really the intended use for it, but it is more concise)
您还可以通过与自身合并并传递一个块来处理新值来进行合并(尽管这不是它的真正用途,但它更简洁)
params.merge(params){|k, v| v.blank? ? nil : v}

