Выполнить все проверки, кроме любого типа "вид": присутствие - PullRequest
0 голосов
/ 19 февраля 2012

Есть ли способ запустить только проверки определенного типа?

У меня есть приложение, которое обновляет несколько экземпляров класса в одной форме.Проверки выполняются путем создания экземпляра 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.

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

Ответы [ 2 ]

1 голос
/ 19 февраля 2012

Я думаю, что обход определенных проверок - интересная идея, но я думаю, что есть более простой способ. Я бы справился с этим, написав собственный метод, который проверяет пакетные обновления, что-то вроде этого:

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 голосов
/ 20 февраля 2012

Ничего себе, после многочасовой работы над этой проблемой она выглядит очень сложной.

Создайте экземпляр класса @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?'

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...