0

多くの sを持つEntryモデルがあります。s は、仮想属性を介してフォームのテキスト ボックスに入力することでエントリに追加されます。モデルで検証する前に、tag_names 文字列はを使用して実際のオブジェクトに変換されます。このモデルには、タグ名が関連付けを介して実行される正規表現と一致することを確認するための検証もあります。TagTagtag_namesEntryTagfind_or_create_by_nameTag

私のエントリーモデルは次のようになります。

class Entry < ActiveRecord::Base
  has_many :entry_tags
  has_many :tags, :through => :entry_tags

  before_validation :update_tags

  attr_writer :tag_names

private
  def update_tags
    if @tag_names
      self.tags = @tag_names.split(",").uniq.map do |name|
        Tag.find_or_create_by_name(name.strip)
      end
    end
  end
end

新しいEntryオブジェクトを作成してタグを割り当てると、すべてが正しく機能します。Tagの 1 つに検証エラーがある場合、タグは保存されず、エラー メッセージが返されます。ただし、メッセージを返す代わりに、既存の Entry オブジェクトを無効なタグで更新しようとすると、self.tags=(上記の) 呼び出しでupdate_tags検証エラー メッセージとともに例外がスローされます。find_or_create_by_nameを呼び出す代わりに実際に新しいオブジェクトを返すように上書きしてもcreate、同じ結果が得られます。

オブジェクトが既に存在する場合、メインレコードが保存される前に、呼び出しが実際にオブジェクトを保存しているように思えます(そしてドキュメントが裏付けているようです) 。この保存が発生しないようにするために、または例外が発生して保存が false を返すのを防ぐためにできることはありますか?tags=TagEntry

4

2 に答える 2

2

私はこのようなことを試してみます:

class Entry < ActiveRecord::Base
  has_many :entry_tags
  has_many :tags, :through => :entry_tags

  before_validation :update_tags

  attr_writer :tag_names
  validates_associated :tags

private
  def update_tags
    return unless @tag_names
    current_tag_names = tags.map(&:name)
    user_tag_names = @tag_names.split(",").uniq
    #add in new tags
    user_tag_names.each do |name|
      next if current_tag_names.include?(name)
      tags.build :name => name
    end
    #remove dropped tags
    ( current_tag_names - user_tag_names ).each do |name|
      removed_tag = tags.find_by_name(name)
      tags.delete(removed_tag)
    end
  end
end

この方法では、アクションで関連するモデルのみを初期化するupdate_tagsため、検証エラーは発生しません。validates_associated :tagsまた、これらの関連モデルのエラーを を使用して標準入力フォームから報告できるように も追加しましたerror_messages_for :entry

削除されたタグを削除するために含まれているコードを更新します。

于 2009-07-15T03:23:57.443 に答える