2012-06-08 10 views
26

ネストされたモデルを破棄したい場合、その属性が親モデルのフォームで空白になっている場合 - モデルが空白の場合はActiveRecord::Callbacksが呼び出されないようです。空のネストされた属性に破棄

class Artist < ActiveRecord::Base 
    using_access_control 
    attr_accessible :bio, :name, :tour_dates_attributes 
    has_many :tour_dates, :dependent => :destroy 
    accepts_nested_attributes_for :tour_dates, :reject_if => lambda { |a| a[:when].blank? || a[:where].blank? }, :allow_destroy => true 
    validates :bio, :name :presence => true 

    def to_param 
    name 
    end 
end 

class TourDate < ActiveRecord::Base 
    validates :address, :when, :where, :artist_id, :presence => true 
    attr_accessible :address, :artist_id, :when, :where 
    belongs_to :artist 
    before_save :destroy_if_blank 

    private 
    def destroy_if_blank 
    logger.info "destroy_if_blank called" 
    end 
end 

私が編集して、新しいツアー日程を追加するために働くアーティストの関連するツアー日程のためのフィールドを表示するためにfields_forを使用するアーティストのためのフォームを、持っているが、私は、単に空白の場合ツアーの日付を削除すると、destroy_if_blankは呼び出されません。おそらくアーティストコントローラの@artist.update_attributes(params[:artist])行は、更新する価値のある空白のエンティティとはみなされません。

何か不足していますか?これを回避する方法はありますか?

答えて

5

'where'または 'when'が空白の場合、accepts_nested _attributes行でreject_ifを削除し、destroy_if空白が呼び出される可能性がある場合、レコードを無視する必要があるというコードがあります。

一般的に破壊しない、あなたは、ちょうどこの今日の一部の繭を使用ドキュメントhttp://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.htmlまた

、チェックアウト、ネストされたレコードに_destroy属性を設定し、それは素晴らしいと思ったでしょうhttps://github.com/nathanvda/cocoon

1

に渡されたreject_ifオプションのため、現在のコードでは不可能です。

Christ Mohrによれば、最も簡単な方法は、親を更新するときにネストされたモデルの属性を_destroyに設定することです。ネストされたモデルは破棄されます。詳細については、ドキュメントを参照するか、this railscastを参照してください。

または、繭やawesome_nested_fieldsのような宝石を使用できます。

具体的には、reject_ifオプションを削除し、親オブジェクト内のコールバック内のロジックを処理する必要があります。 tour_dates_attributes内の空白値をチェックし、ネストされたモデルを破棄する必要があります。しかし、慎重に踏み込んでください...

5

私は今日このようなことをすることができました。 @shuriuさんのように、あなたの最善の選択肢は、reject_ifオプションを削除し、自分で破壊を処理することです。 mark_for_destructionに便利です:ブロックreject_ifが、挿入:

class Artist < ActiveRecord::Base 
    accepts_nested_attributes_for :tour_dates 

    before_validation :mark_tour_dates_for_destruction 

    def mark_tour_dates_for_destruction 
    tour_dates.each do |tour_date| 
     if tour_date.when.blank? or tour_date.where.blank? 
     tour_date.mark_for_destruction 
     end 
    end 
    end 
end 
+0

なぜ「tour_date.when.blank」ですか?二度?ありがとう –

+0

@maxkaplan:それは 'when'と' where'だったはずです。私は答えでそれを修正しました。ありがとう! – Sunny

61

私は続けるだろう、あなたの条件が満たされた場合、属性に_destroy => 1ハッシュ。 (これは、フォームコードに_destroyを追加するのが便利でない場合に便利です)

正しい値を返すためにレコードが存在するかどうかを確認するために追加のチェックが必要ですが、すべての場合私のために。スティーブケンウォーシーの答えと同様に

empty = attributes.except(:id).values.all?(&:blank?) 
+0

これはすばらしい解決策ですが、関連するモデルの検証がオフになっていることを確認するか、新しいレコードで失敗するようにする必要があります。 – JoshL

+3

新しいレコードは 'id'属性を持たないはずで、入れ子にされたレコードは拒否され、ロードされないので、新しいレコードでは失敗しません。この解決策は揺れる!私は 'empty = attributes.reject {| k、v | k == 'id'}。values.all?(&:空白?) 'すべての空の属性をチェックします。 – DGM

+2

私は今あなたが大好きです –

2

、なしのローカル変数:すべての属性がちょうどにempty計算を変更することにより、空白のとき

accepts_nested_attributes_for :tour_dates, :reject_if => :reject_tour, :allow_destroy => true 

def reject_tour(attributes) 
    exists = attributes['id'].present? 
    empty = attributes.slice(:when, :where).values.all?(&:blank?) 
    attributes.merge!({:_destroy => 1}) if exists and empty # destroy empty tour 
    return (!exists and empty) # reject empty attributes 
end 

あなたが適用できます。

accepts_nested_attributes_for :tour_dates, :reject_if => :reject_tour, :allow_destroy => true 

def reject_tour(attributes) 
    if attributes[:when].blank? || attributes[:where].blank? 
     if attributes[:id].present? 
     attributes.merge!({:_destroy => 1}) && false 
     else 
     true 
     end 
    end 
    end 
+0

ボラットの言葉に「とてもいいです」 –

関連する問題