1

これは私のモデルの簡素化されたバージョンです。

model Paper
  PAPER_STARTING_NUMBER = 1

  validate_uniqueness_of :number, :allow_blank => true
  before_create :alocate_paper_number

  def alocate_paper_number
    return true if self.number.present?
    p_number = Paper.maximum('number') || Paper::PAPER_STARTING_NUMBER 
    self.number = p_number >= Paper::PAPER_STARTING_NUMBER ? p_number+1 : Paper::PAPER_STARTING_NUMBER 
    return true 
  end
end

問題は、番号列に重複があることです。callback を変更せずにこれを修正できる理由と方法についてのアイデア。データベースに一意性の検証を追加したり、その列にシーケンスを作成したりできますが、他のアイデアはありますか?

4

3 に答える 3

2

それはドキュメントにあります。一意にするためのTRIESのvalidate_uniqueness_。ただし、2つのプロセスが同時に1つのレコードを追加する場合、両方に同じ番号を含めることができます。

一意性を保証したい場合は、データベースに任せてください。ただし、これはDBごとに異なるため、Railsは設計上サポートしていません。

ここで説明されています:http://guides.rubyonrails.org/active_record_validations_callbacks.html#uniqueness

解決策:「これを回避するには、データベースに一意のインデックスを作成する必要があります。」

于 2012-08-13T10:09:41.523 に答える
2

まず、コールバックの順序を理解する必要があります:

(-) 保存

(-) 有効

(1) before_validation

(-) 検証

(2) after_validation

(3) before_save

(4) before_create

(-) 作成

(5) after_create

(6) after_save

(7) after_commit

ご覧のとおり、それはnumber属性の一意性を検証し、次に before_create を自由に使用して、検証が達成したいことに反することができます。

よりクリーンなアーキテクチャに関しては、ユーザーが数を選択できるようには見えないため、これらのアイデアの両方をカスタム モデルにまとめます。それは単なるインクリメンターですよね?

def alocate_paper_number
  p_number = Paper.maximum('number') || Paper::PAPER_STARTING_NUMBER 
  self.number = p_number + 1
end

そのスニペットだけで、常に上向きにインクリメントされるため(私が気付いていない逆方向に数値が移動する可能性がない限り)、重複を防ぐことができます。また、これらすべての true を返す理由もありません。その真実は十分です!

于 2012-08-13T09:53:04.377 に答える
1

どのように修正したか(検証エラーを返すことができなかったことを心に留めておいてください)(muとHugoが提案したように)数値列に一意性インデックスを追加しました。コントローラーで検証エラーを返すことができなかったためです。

class PaperController < ApplicationController
  def create
    begin
      @paper.save
    rescue ActiveRecord::RecordNotUnique
      @paper.number = nil
      create
    end
  end
end
于 2012-08-14T11:33:40.267 に答える