2010-02-22 28 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)>}