2012-02-19 3 views
0

Есть ли способ запуска только проверки определенного типа?Запустить все проверки За исключением любого типа «вид»: наличие

У меня есть приложение, которое обновляет несколько экземпляров класса в одной форме. Проверки выполняются путем создания экземпляра здания и проверки на этом.

Проблема: если атрибут не обновляется, поле формы остается пустым, а форма отправляет пустую строку. Здесь вы можете увидеть пример, где params [: building] [: name] - пустая строка.

params = {: building => {: name => "",: short_name => "",: code => "test"},: commit => "Update Buildings",: 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 

Это метод, используемый г болеешь бежать валидации: 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.

Я хотел бы услышать любые ваши идеи.

ответ

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 в конце, - это наличие существующих плохих данных.

+0

Спасибо за предложение. К сожалению, похоже, что такой хак нужен. Это делает для грязного кода, хотя, особенно если вы хотите разрешить «массовые изменения» для нескольких моделей. Используя вашу идею, я создал более обобщенное решение, которое я опубликую. – nslocum

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?' 

Я надеюсь, что это может оказаться полезным для других организаций, работающих на подобных «обновления объемных» вопросы проверки.

Смежные вопросы