2012-02-19 3 views
0

特定のタイプの検証のみを実行する方法はありますか?"kind"以外のすべての検証を実行する:

複数のクラスインスタンスを1つのフォームで更新するアプリケーションがあります。検証は、という建物を作成してという建物を作成し、検証します。

問題:属性が更新されていない場合、フォームフィールドは空白のままになり、フォームは空の文字列を送信します。ここでparams [:building] [:name]が空の文字列である例を見ることができます。 "ビルドの更新"、:building_ids => ["ビルドの更新"、 "ビルドの更新"、 "ビルドの更新" :コントローラ=>「建物」}

属性の存在をチェックするものを除くすべての検証を実行するにはどうすればよいですか?私はこれに取り組んでかなりの時間を費やし、そしてここで私がこれまでに発見したものだき

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 

これはrで使用される方法であります検証を実行するための不振の解決策: 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?' 

は、私は、これは同様の「一括更新」の検証の問題に取り組んで他の人に助けになることを願って。

関連する問題