Questionクラスがあります:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
特定のユーザーは1日に1つの質問しか作成できないため、一意のインデックスを介してデータベースの一意性を、validates_uniqueness_ofを介してQuestionクラスを強制する必要があります。
私が直面している問題は、管理者以外のユーザーにのみその制約が必要なことです。そのため、管理者は1日に必要なだけ質問を作成できます。それをエレガントに達成するためのアイデアはありますか?
実行するRubyの単純な文字列、Proc、またはメソッド名をシンボルとして値として:if
または:unless
のいずれかに渡すことにより、検証を条件付きにすることができます検証:次に例を示します。
Railsバージョン5.2の前に、文字列を渡すことができます。
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
5.2以降、文字列はサポートされなくなり、次のオプションが残されています。
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
あなたの場合、次のようなことができます:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
詳細については、Railsガイドの条件検証セクションをご覧ください。 http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
一意性を保証する唯一の方法は、データベース(一意のインデックスなど)を使用することです。 Railsのみに基づくすべてのアプローチには、競合状態が含まれます。あなたの制約を考えると、管理者用にnull
を残す日とユーザーIDの組み合わせを含む、個別に一意にインデックス付けされた列を確立することが最も簡単だと思います。
はどうかと言うと validates_uniqueness_of
、 http://apidock.com/Rails/ActiveRecord/Validationsで説明されているように、if
またはunless
オプションを使用して、検証を非管理者に制限できます。/ClassMethods/validates_uniqueness_of
validates_uniqueness_of
呼び出しに条件を追加するだけです。
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
しかし、さらに良いのは、カスタム検証を作成するだけです:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end