2010-02-22 31 views
19

我在我的Rails應用程序中使用Clearance進行身份驗證。 mixin Clearance::User爲我的User模型添加了一些驗證,但有一個我想刪除或覆蓋。這樣做的最好方法是什麼?刪除或覆蓋由超類或mixin添加的ActiveRecord驗證

有問題的驗證是

validates_uniqueness_of :email, :case_sensitive => false 

這本身並不壞,但我需要添加:scope => :account_id。問題是,如果我添加這對我User模型

validates_uniqueness_of :email, :scope => :account_id 

我得到驗證,和一個間隙增加了比我更多的限制,所以我的沒有任何影響。我需要確保只有我的跑步。我該怎麼做呢?

回答

6

我結束了「解決」與下面的技巧問題:

  1. 外觀上的錯誤類型的:email屬性:taken
  2. 檢查郵件是否是唯一的此帳戶(這是驗證我想要做的)
  3. 如果電子郵件對於此帳戶是唯一的,請刪除該錯誤。

聽起來很合理,直到您閱讀代碼並發現我如何刪除錯誤。 ActiveRecord::Errors沒有方法來刪除一旦添加錯誤,所以我必須抓住它的內部並自己做。超級醜女超級醜陋。

這是代碼:

def validate 
    super 
    remove_spurious_email_taken_error!(errors) 
end 

def remove_spurious_email_taken_error!(errors) 
    errors.each_error do |attribute, error| 
    if error.attribute == :email && error.type == :taken && email_unique_for_account? 
     errors_hash = errors.instance_variable_get(:@errors) 
     if Array == errors_hash[attribute] && errors_hash[attribute].size > 1 
     errors_hash[attribute].delete_at(errors_hash[attribute].index(error)) 
     else 
     errors_hash.delete(attribute) 
     end 
    end 
    end 
end 

def email_unique_for_account? 
    match = account.users.find_by_email(email) 
    match.nil? or match == self 
end 

如果有人有更好的辦法知道,我將非常感激。

+1

鐵軌3的,則可以使用errors.delete(:字段)以從集合中刪除一個錯誤。 – 2013-04-09 18:49:09

4

我最近有這個問題,谷歌沒有給我足夠快的答案後,我發現一個整潔但仍然不理想的解決方案,以解決這個問題。現在這不一定適用於你的情況,因爲它似乎是你使用預先存在的超類,但對我來說,它是我自己的代碼,所以我只用了一個:if參數和超類中的類型檢查。

def SuperClass 
    validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| !(obj.is_a? SubClass)} 
end 

def SubClass < SuperClass 
    validates_such_and_such_of :attr 
end 

在multpile子類的情況下

def SuperClass 
    validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| [SubClass1, SubClass2].select{|sub| obj.is_a? sub}.empty?} 
end 

def SubClass1 < SuperClass 
    validates_such_and_such_of :attr 
end 

def SubClass2 < SuperClass 
end 
+1

不幸的是我不能改變超類,它在寶石中。 – Theo 2010-07-23 05:05:15

+0

這就像把磁帶放在你的檢查引擎燈上,並說它起作用。 – courtsimas 2016-06-02 14:41:22

-1

這裏有一個Rails 3的 「解決方案」 爲我工作(如果再有人有更好的辦法,請提供它!)

class NameUniqueForTypeValidator < ActiveModel::Validator 

    def validate(record) 
    remove_name_taken_error!(record) 
    end 

    def remove_name_taken_error!(record) 
    errors = record.errors 
    errors.each do |attribute, error| 
     if attribute == :name && error.include?("taken") && record.name_unique_for_type? 
     errors.messages[attribute].each do |msg| 
      errors.messages[attribute].delete_at(errors.messages[attribute].index(msg)) if msg.include?("taken") 
     end 
     errors.messages.delete(attribute) if errors.messages[attribute].empty? 
     end 
    end 
    end 

end 


ActsAsTaggableOn::Tag.class_eval do 
    validates_with NameUniqueForTypeValidator 

    def name_unique_for_type? 
    !ActsAsTaggableOn::Tag.where(:name => name, :type => type).exists? 
    end 
end 
1

我知道我遲到了,但是怎麼樣:

module Clearance 
    module User 
    module Validations 
     extend ActiveSupport::Concern 

     included do 
     validates :email, 
      email: true, 
      presence: true, 
      uniqueness: { scope: :account, allow_blank: true }, 
      unless: :email_optional? 

     validates :password, presence: true, unless: :password_optional? 
     end 
    end 
    end 
end 
初始化程序中的

1

Errors.delete(key)刪除屬性的所有錯誤,我只想刪除屬於某個屬性的特定類型的錯誤。以下方法可以添加到任何模型。

返回消息,如果刪除,否則爲零。內部數據結構被修改,因此所有其他方法在錯誤消除後應按預期工作。

MIT License

方法發佈驗證已運行後,從模型中刪除錯誤。

def remove_error!(attribute, message = :invalid, options = {}) 
    # -- Same code as private method ActiveModel::Errors.normalize_message(attribute, message, options). 
    callbacks_options = [:if, :unless, :on, :allow_nil, :allow_blank, :strict] 
    case message 
    when Symbol 
    message = self.errors.generate_message(attribute, message, options.except(*callbacks_options)) 
    when Proc 
    message = message.call 
    else 
    message = message 
    end 
    # -- end block 

    # -- Delete message - based on ActiveModel::Errors.added?(attribute, message = :invalid, options = {}). 
    message = self.errors[attribute].delete(message) rescue nil 
    # -- Delete attribute from errors if message array is empty. 
    self.errors.messages.delete(attribute) if !self.errors.messages[attribute].present? 
    return message 
end 

用法:

user.remove_error!(:email, :taken) 

方法來檢查有效性,除了指定的屬性和消息。

def valid_except?(except={}) 
    self.valid? 
    # -- Use this to call valid? for superclass if self.valid? is overridden. 
    # self.class.superclass.instance_method(:valid?).bind(self).call 
    except.each do |attribute, message| 
    if message.present? 
     remove_error!(attribute, message) 
    else 
     self.errors.delete(attribute) 
    end 
    end 
    !self.errors.present? 
end 

用法:

user.valid_except?({email: :blank}) 
user.valid_except?({email: "can't be blank"}) 
-1

對我來說,下面的代碼我的模型已經足夠了。我不希望郵政編碼驗證。

after_validation :remove_nonrequired 

def remove_nonrequired 
    errors.messages.delete(:zipcode) 
end 
+3

你在開玩笑吧? – 2015-02-05 16:17:21

+0

對我來說,刪除了錯誤,但不是模型上的「無效」狀態。 – PJSCopeland 2017-08-29 22:49:15

4

我需要刪除狂歡產品性能:value驗證,它似乎有一個與Klass.class_evalAciveRecord::Base

module Spree 
    class ProductProperty < Spree::Base 

    #spree logic 

    validates :property, presence: true 
    validates :value, length: { maximum: 255 } 

    #spree logic 


    end 
end 

一個simplier解決方案clear_validators!這裏覆蓋它

Spree::ProductProperty.class_eval do  
    clear_validators! 
    validates :property, presence: true 
end 
+0

是否不會刪除基類上的驗證程序,因此也會刪除其他所有子類? – 2016-02-03 07:36:20

+0

根據https://github.com/rails/rails/blob/e3ceb28e66ca6e869f8f9778dc42672f48001a90/activemodel/lib/active_model/validations.rb#L233它只是重置本地實例化的驗證/回調集。 我認爲回調的設置是在單個類而不是所有鏈上設置的。我沒有派生類的驗證問題。 – 2016-02-03 18:57:52

+0

我認爲它應該像這樣:'class Subclass 2016-02-04 07:15:37

8

我會分叉創業板,並添加一個簡單的檢查,然後可以被覆蓋。我的例子使用關注。

關注:

module Slugify 

    extend ActiveSupport::Concern 

    included do 

    validates :slug, uniqueness: true, unless: :skip_uniqueness? 
    end 

    protected 

    def skip_uniqueness? 
    false 
    end 

end 

型號:

class Category < ActiveRecord::Base 
    include Slugify 

    belongs_to :section 

    validates :slug, uniqueness: { scope: :section_id } 

    protected 

    def skip_uniqueness? 
    true 
    end 
end 
0

在Rails 4,你應該能夠使用skip_callback(:validate, :name_of_validation_method) ...如果你一個方便的命名驗證方法。 (免責聲明:我還沒有測試過。)如果不是,您需要進入回調列表以找到您想跳過的回調列表,並使用其filter對象。

例子:

我正在使用Rails 4.1.11和狂歡2.4.11.beta一個網站,已經從2.1.4升級大禮包。爲了歷史目的,我們的代碼將Spree::Variant的多個副本存儲在一張表中。

自升級以來,寶石現在validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) },這打破了我們的代碼。正如你會注意到的,雖然它並沒有使用命名方法來實現。這是我在一個Spree::Variant.class_eval塊已經完成:

unique_sku_filter = _validate_callbacks.find do |c| 
    c.filter.is_a?(ActiveRecord::Validations::UniquenessValidator) && 
    c.filter.instance_variable_get(:@attributes) == [:sku] 
end.filter 

skip_callback(:validate, unique_sku_filter) 

這似乎移除Variant的鏈完全回調。

注意:我必須使用instance_variable_get作爲@attributes,因爲它沒有訪問者。您也可以在find塊中檢查c.filter.options;在上面的例子中,這看起來像:

c.filter.options 
#=> {:case_sensitive=>true, :allow_blank=>true, :conditions=>#<Proc:... (lambda)>}