有没有办法只运行特定类型的验证?
我有一个应用程序可以在一个表单中更新多个类实例。通过创建 building 的实例并对其进行验证来执行验证。
问题:如果没有更新属性,则表单字段留空,表单提交一个空字符串。你可以在这里看到一个例子,其中params [:building] [:name]是一个空字符串。
params = {:building => {:name =>“”,:short_name =>“”,:code =>“test”},:commit =>“更新建筑物”,:building_ids => [“2”,“5”,“7”],:action =>“update_multiple”,:controller =>“buildings”}
如何运行除检查是否存在属性的验证之外的所有验证?
def update_multiple
@building = Building.new(params[:building].reject {|k,v| v.blank?})
respond_to do |format|
if @building.valid?
Building.update_all( params[:building].reject {|k,v| v.blank?}, {:id => params[:building_ids]} )
format.html { redirect_to buildings_path, notice: 'Buildings successfully updated.' }
else
@buildings = Building.all
format.html { render action: 'edit_multiple' }
end
end
end
我花了很多时间研究这个问题,这是我迄今为止所发现的:
检索模型验证
$ Building.validators
=> [#<ActiveModel::Validations::PresenceValidator:0x007fbdf4d6f0b0 @attributes=[:name], @options={}>]
获取验证者种类
$ Building.validators[0].kind
=> :presence
这是rails用于运行验证的方法: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb 第353行
# This method runs callback chain for the given kind.
# If this called first time it creates a new callback method for the kind.
# This generated method plays caching role.
#
def __run_callbacks(kind, object, &blk) #:nodoc:
name = __callback_runner_name(kind)
unless object.respond_to?(name, true)
str = object.send("_#{kind}_callbacks").compile
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}() #{str} end
protected :#{name}
RUBY_EVAL
end
object.send(name, &blk)
end
如果有办法直接运行验证?如果是这样,我可以遍历Building.validators
,只运行kind != :presence
。
我很想听听你的任何想法。
答案 0 :(得分:1)
我认为绕过特定的验证是一个有趣的想法,但我认为这是一种更简单的方法。我会通过编写一个验证批量更新的自定义方法来处理这个问题,如下所示:
def valid_for_batch?(params)
@building = Building.new(params[:building].reject {|k,v| v.blank?})
@building.name = "Foo" if @building.name.blank?
@building.shortname = "Bar" if @building.shortname.blank?
# etc...
@building.valid?
end
只需确保“Foo”和“Bar”上有值将通过验证 - 所有代码正在查看值是否为空,如果是,请将其替换为临时值通过验证。这样,@building.valid?
最终返回false的唯一方法就是存在不良数据。
答案 1 :(得分:0)
创建一个类实例@building
,并为已验证其存在的属性设置占位符。
我尝试了很多不同的方法,这是迄今为止我能想到的最好方法。
def update_multiple
valid = true
@building = Building.new(params[:building])
set_bulk_edit_placeholders_for_presence_validators(@building)
building_valid = @building.valid?
# Check if buildings were selected to edit
if params[:building_ids].blank?
valid = false
@building.errors.add(:base, 'You must select at least one Building')
end
# Check if all form fields are blank
if params[:building].values.delete_if {|v| v.blank?}.blank?
valid = false
@building.errors.add(:base, 'The edit form must not be empty')
end
respond_to do |format|
if valid && building_valid
@buildings = Building.find(params[:building_ids])
@buildings.each do |building|
building.update_attributes!(params[:building].reject { |k,v| v.blank? })
end
format.html { redirect_to buildings_path, notice: 'Buildings were successfully updated.' }
else
@buildings = Building.all
format.html { render edit_multiple_buildings_path }
end
end
end
这是设置占位符的一般功能。它可以用于任何控制器的任何模型。
application_controller.rb
private
def set_bulk_edit_placeholders_for_presence_validators(class_instance, hash={})
model_name = hash[:model_name] || self.class.name.sub("Controller", "").singularize.downcase
klass = self.class.name.sub("Controller", "").singularize.constantize
klass.send(:validators).each { |validator|
if (validator.kind == :presence)
validator.attributes.each { |attribute|
if params[model_name][attribute].blank?
class_instance.send("#{attribute}=", 'placeholder')
end
}
end
}
end
为了在表单上正确显示错误,它必须是form_for @buildings
。
由于占位符是为@buildings对象设置的,因此我们必须指定表单值直接来自params
。
edit_multiple.haml
= bootstrap_form_for @building, :url => update_multiple_buildings_path, :method => :put, :html => {:class => 'form-horizontal'} do |f|
= f.text_field :name, :value => params[:building].try(:send, :[], :name)
= f.text_field :short_name, :value => params[:building].try(:send, :[], :short_name)
= f.text_field :code, :value => params[:building].try(:send, :[], :code)
= f.submit 'Update Buildings', :name => nil
%table.table.table-striped.table-bordered
%thead
%tr
%th.check_box_column= check_box_tag 'select_all'
%th.sortable= sortable :name
%th Abbr Name
%th.sortable= sortable :code
%th
%tbody
- @buildings.each do |building|
%tr
%td.check_box_column= check_box_tag 'building_ids[]', building.id, (params[:building_ids].include?(building.id.to_s) if params[:building_ids])
%td= building.name
%td= building.short_name
%td= building.code
%td.links.span2
= link_to 'Show', building
= link_to 'Edit', edit_building_path(building)
= link_to 'Delete', building, :method => :delete, :confirm => 'Are you sure you want to delete this building?'
我希望这对处理类似“批量更新”验证问题的其他人有所帮助。