51

ユーザーがフォームを介して入力した7つの数値属性を持つRailsモデルがあります。

これらの各属性の存在を検証する必要があります。これは明らかに簡単に使用できます。

validates :attribute1, :presence => true
validates :attribute2, :presence => true
# and so on through the attributes

ただし、いくつかの属性を取得し、それらを使用していくつかの計算を行うカスタムバリデーターも実行する必要があります。これらの計算の結果が特定の範囲内にない場合は、モデルを無効と宣言する必要があります。

それ自体で、これも簡単です

validate :calculations_ok?

def calculations_ok?
  errors[:base] << "Not within required range" unless within_required_range?
end

def within_required_range?
  # check the calculations and return true or false here
end

ただし、問題は、メソッド「validate」が常にメソッド「validates」の前に実行されることです。これは、ユーザーが必須フィールドの1つを空白のままにすると、空白の属性を使用して計算を行おうとすると、railsがエラーをスローすることを意味します。

では、最初に必要なすべての属性の存在を確認するにはどうすればよいですか?

4

5 に答える 5

21

attributesハッシュ自体がどのように順序付けられるかに依存する可能性があるため、これらの検証が実行される順序が保証されているかどうかはわかりません。validateメソッドの復元力を高め、必要なデータの一部が欠落している場合は単に実行しない方がよい場合があります。例えば:

def within_required_range?
  return if ([ a, b, c, d ].any?(&:blank?))

  # ...
end

これは、nil、空の配列または文字列などを含む変数のいずれかaが空白の場合に解決されます。d

于 2011-05-11T14:55:07.997 に答える
9

もう少し複雑な状況の代替手段は、最初に依存属性の検証を実行するヘルパーメソッドを作成することです。次に、:calculations_okを作成できますか?検証は条件付きで実行されます。

validates :attribute1, :presence => true
validates :attribute2, :presence => true
...
validates :attribute7, :presence => true

validate :calculations_ok?, :unless => Proc.new { |a| a.dependent_attributes_valid? }

def dependent_attributes_valid?
  [:attribute1, ..., :attribute7].each do |field|
    self.class.validators_on(field).each { |v| v.validate(self) }
    return false if self.errors.messages[field].present?
  end
  return true
end

依存属性の検証が非常に複雑だったため、プロジェクト用にこのようなものを作成する必要がありました。私の同等の:calculations_ok?依存属性が適切に検証されなかった場合、例外がスローされます。

利点:

  • 特に検証が複雑な場合は、比較的乾燥します
  • エラー配列が、マクロ検証ではなく、正しく失敗した検証を報告することを保証します
  • 後で追加する依存属性に対する追加の検証が自動的に含まれます

警告:

  • すべての検証を2回実行する可能性があります
  • 依存属性に対してすべての検証を実行したくない場合があります
于 2012-07-16T20:47:59.580 に答える
2

http://railscasts.com/episodes/211-validations-in-rails-3をチェックしてください

カスタムバリデーターを実装した後は、次のようにします。

validates :attribute1, :calculations_ok => true

それはあなたの問題を解決するはずです。

于 2011-05-11T14:50:43.040 に答える
1

JamesHソリューションは私にとって最も理にかなっています。ただし、考慮すべきもう1つのことは、依存検証に条件がある場合、dependent_attributes_validを確認するためにもそれらをチェックする必要があるということです。仕事への呼び出し。

すなわち。

    validates :attribute1, presence: true
    validates :attribute1, uniqueness: true, if: :attribute1?
    validates :attribute1, numericality: true, unless: Proc.new {|r| r.attribute1.index("@") }
    validates :attribute2, presence: true
    ...
    validates :attribute7, presence: true

    validate :calculations_ok?, unless: Proc.new { |a| a.dependent_attributes_valid? }

    def dependent_attributes_valid?
      [:attribute1, ..., :attribute7].each do |field|
        self.class.validators_on(field).each do |v|
          # Surely there is a better way with rails?
          existing_error = v.attributes.select{|a| self.errors[a].present? }.present?

          if_condition = v.options[:if]
          validation_if_condition_passes = if_condition.blank?
          validation_if_condition_passes ||= if_condition.class == Proc ? if_condition.call(self) : !!self.send(if_condition)

          unless_condition = v.options[:unless]
          validation_unless_condition_passes = unless_condition.blank?
          validation_unless_condition_passes ||= unless_condition.class == Proc ? unless_condition.call(self) : !!self.send(unless_condition)

          if !existing_error and validation_if_condition_passes and validation_unless_condition_passes
            v.validate(self)
          end
        end
        return false if self.errors.messages[field].present?
      end
      return true
    end
于 2016-04-03T23:49:17.570 に答える
1

かなり前にこの問題が発生したことを思い出しますが、検証の順序を設定でき、検証でエラーが返された場合に実行チェーンを停止できるかどうかはまだ不明です。

Railsがこのオプションを提供しているとは思いません。それは理にかなっている; レコード上のすべてのエラー(無効な入力、検証のために失敗した後に発生するエラーを含む)を表示したいと思います。

考えられるアプローチの1つは、検証する入力が存在する場合にのみ検証することです。

def within_required_range?
  return unless [:attribute1, attribute2, ..].all?(&:present?)

  # check the calculations and return true or false here
end

Railsの慣用的な検証オプションを使用して、それをより美しく、より適切に構造化(単一責任)します。

validates :attribute1, :presence => true
validates :attribute2, :presence => true
# and so on through the attributes

validate :calculations_ok?, if: :attributes_present?

private
  def attributes_present?
    [:attribute1, attribute2, ..].all?(&:present?)
  end

  def calculations_ok?
    errors[:base] << "Not within required range" unless within_required_range?
  end

  def within_required_range?
    # check the calculations and return true or false here
  end
于 2020-03-24T15:59:42.490 に答える