我的模型Cart
与has_many
有cart_items
的关系。
# cart.rb:
accepts_nested_attributes_for :cart_items, allow_destroy: true
has_many :cart_items, dependent: :destroy, inverse_of: :cart
# cart_item.rb:
validates :quantity, presence: true, numericality: { greater_than: 0 }
# Controller code:
def update
current_cart.assign_attributes(params[:cart])
.....
current_cart.valid?
end
更新cart_items时,如果数量的整数(to_i
)值与旧值相同,则验证无效。
例如,
如果quantity
的旧值为4,则现在新值更新为4abc,然后数量验证不起作用且记录被视为有效。
虽然,如果将新值从4更新为5abc,则会按预期显示验证错误。
为什么会这一切发生的任何建议?
编辑1:
这是rails console的输出:
[3] pry(#<Shopping::CartsController>)> cart
=> #<Cart id: 12, created_at: "2017-06-22 13:52:59", updated_at: "2017-06-23 08:54:27">
[4] pry(#<Shopping::CartsController>)> cart.cart_items
[#<CartItem id: 34201, cart_id: 12, quantity: 4, created_at: "2017-06-23 05:25:39", updated_at: "2017-06-23 08:54:27">]
[5] pry(#<Shopping::CartsController>)> param_hash
=> {"cart_items_attributes"=>{"0"=>{"id"=>"34201", "quantity"=>"4abc"}}}
[6] pry(#<Shopping::CartsController>)> cart.assign_attributes param_hash
=> nil
[7] pry(#<Shopping::CartsController>)> cart.valid?
=> true
此处,cart_item
的先前数量为4,新值为4abc,但购物车仍然有效。
编辑2:
我已经检查了How to validate numericality and inclusion while still allowing attribute to be nil in some cases?的答案,因为它在评论中被屏蔽为重复,但它似乎无效。
正如我上面提到的,如果新数量的to_i
与之前的数量不同,验证工作正常,但如果它相同则验证不起作用。
此外,我尝试使用validate
应用自定义验证方法,但我在该方法中获得to_i
值。类似的东西:
型号代码:
validate :validate_quantity
# quantity saved in db => 4
# new quantity in form => 4abc
def validate_quantity
puts quantity_changed? # => false
puts quantity # => 4
end
编辑3:
似乎新值的to_i
与先前值相同,然后模型将值转换为整数并考虑甚至没有更新的字段。
编辑4:
我得到答案&amp;关于to_i
的目的的评论。我知道to_i
做了什么。
我只是想知道为什么我 不 如果新数量的to_i
与数据库中存储的数量相似,则会收到验证错误。
我知道quantity
列是integer
而ActiveRecord
会将其转换为整数但是必须存在验证错误,因为我已在模型中添加了该错误。
如果新值的
to_i
与db中存储的quantity
不同,我将收到验证错误。
BUT
如果新值
to_i
与db中存储的quantity
相同,我 <\ n> 获取验证错误。
答案 0 :(得分:0)
由于quantity
列定义为integer
,因此在将其分配给属性之前,activerecord会将其与.to_i
进行类型转换,但是,quantity_changed?
在这种情况下将为真与上面显示的不同,因此为您提供解决方案1的提示,否则您可以检查param是否仅在控制器中包含整数,如解决方案2中那样。
解决方案1
validate :validate_quantity
# quantity saved in db => 4
# new quantity in form => '4abc'
def validate_quantity
if quantity_changed?
if quantity_was == quantity || quantity <= 0
errors.add(:base, 'invalid quantity')
return false
else
return true
end
else
true
end
end
解决方案2
在你的控制器中,
before_action :validate_cart_params, only: [:create, :update]
private
def validate_cart_params
unless cart_params[:quantity].scan(/\D/).empty?
render json: { errors: "Oops! Quantity should be numeric and greater than 0." }, status: 422 and return
end
end
<强>更新强>
我用谷歌搜索了一下,但是很晚才找到帮助者_before_type_cast
,这也是一个很好的解决方案,而@Federico已经把它作为一个解决方案。我也将它添加到我的列表中。
解决方案3
validate :validate_quantity
# quantity saved in db => 4
# new quantity in form => '4abc'
def validate_quantity
if quantity_changed? || ((actual_quantity = quantity_before_type_cast) != quantity_was)
if actual_quantity.scan(/\D/).present? || quantity <= 0
errors.add(:base, 'invalid quantity')
return false
else
return true
end
else
true
end
end
答案 1 :(得分:0)
首先回答您的问题为什么:
在历史上可见它的工作方式与今天不同。我个人怀疑这个提交(经过非常简短的搜索):https://github.com/rails/rails/commit/7500daec69499e4f2da2fc06cd816c754cf59504
如何修复那个?
升级你的rails gem ...我可以推荐Rails 5.0.3
,我测试了它并按预期工作。
答案 2 :(得分:0)
在the source code for numeric validation之后,它在字符串
上强制转换Integer类型ng-options
因此输入大于0
要解决此问题,请尝试在视图中使用"4abc".to_i
=> 4
强制用户仅键入整数值
答案 3 :(得分:0)
您应该使用quantity_before_type_cast
,如here中所解释的那样,在&#34;在进行类型转换之前访问属性&#34;。例如:
validate :validate_quantity
# quantity saved in db => 4
# new quantity in form => 4abc
def validate_quantity
q = quantity_before_type_cast
return if q == q.to_i
errors.add(:quantity, 'invalid quantity') unless (q.to_i.to_s == q)
end
答案 4 :(得分:0)
此类问题已在Github中报告。
报告的问题:
已修复:
[v4.0]
[v3.2]
报告所有问题,其中整数属性的先前值为0
,新值为某个字符串(其to_i
将为0)。因此,修复也仅适用于0。
您可以在active_record/attribute_methods/dirty.rb
的{{3}}中查看相同内容,该quantity
最初是从#changes_from_zero_to_string?
调用的
仅针对to_i
字段覆盖#_field_changed?
(由于时间不足。将来,我将覆盖所有整数字段的方法)。
现在,如果某个字母数字quantity
的{{1}}值等于数据库中的当前quantity
值,则下面的方法将返回true
,并且不会输入值。< / p>
def _field_changed?(attr, old, value)
if attr == 'quantity' && old == value.to_i && value != value.to_i.to_s
return true
end
super
end
答案 5 :(得分:-1)
to_i(p1 = v1) public
返回将str中的前导字符解释为整数基数(2到36之间)的结果。超出有效数字末尾的无关字符将被忽略。如果在str的开头没有有效数字,则返回0。当base有效时,此方法永远不会引发异常。
"12345".to_i #=> 12345
"99 red balloons".to_i #=> 99
"0a".to_i #=> 0
"0a".to_i(16) #=> 10
"hello".to_i #=> 0
"1100101".to_i(2) #=> 101
"1100101".to_i(8) #=> 294977
"1100101".to_i(10) #=> 1100101
"1100101".to_i(16) #=> 17826049
问题发生的原因是
"4".to_i => 4
"4abc".to_i => 4
这意味着您的自定义验证将通过,不会在页面上造成任何错误。
希望能帮到你......