我在 Rails 应用程序中使用 Clearance 进行身份验证。这 Clearance::User mixin 为我的添加了一些验证 User 模型,但我想删除或覆盖其中一个。这样做的最佳方法是什么?

有问题的验证是

validates_uniqueness_of :email, :case_sensitive => false

这本身并不坏,但我需要添加 :scope => :account_id. 。问题是,如果我将其添加到我的 User 模型

validates_uniqueness_of :email, :scope => :account_id

我明白了 两个都 验证,并且 Clearance 添加的验证比我的更严格,所以我的没有任何效果。我需要确保只有我的运行。我该怎么做呢?

有帮助吗?

解决方案 2

我最终通过以下技巧“解决”了这个问题:

  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

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

其他提示

我叉的GEM并添加一个简单的检查,然后可以被覆盖。我的示例使用了关注。

关注:

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

最近我有这个问题,此前谷歌没有给我答案速度不够快,我发现了一个更简洁,但仍对这个问题非理想的解决方案。现在,这不会在你的情况一定的工作,因为它似乎使用预先存在的超类,但对我来说这是我自己的代码,所以我只是用一个:如果param用在超类的类型检查

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

我需要除去施普雷产品属性:value验证和它似乎有一个与Klass.class_evalclear_validators!AciveRecord::Base一个simplier溶液

module Spree
  class ProductProperty < Spree::Base

    #spree logic

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

    #spree logic


  end
end

和这里重写它

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

我知道我迟到了比赛,但如何:

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

在一个初始化?

Errors.delete(键)删除所有误差的一个属性,我只希望删除错误的属于一个属性的特定类型。这个下面的方法可以被添加到任何模型。

如果返回消息中去除,否则无。内部数据结构被修改,以便所有其他方法应该误差去除后正常工作。

MIT许可证发布

方法来从模型后验证已运行去除错误。

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"})

在Rails 4,你应该能够使用skip_callback(:validate, :name_of_validation_method) ......如果你的的一个方便的命名验证方法。 。(免责声明:我没有测试过)的如果没有,你需要攻入回调的列表,找到你想跳过一个,并使用其filter对象

示例:

我正在使用站点的Rails 4.1.11和狂欢2.4.11.beta,具有从2.1.4升级狂欢。 Spree::Variants的一个表中我们的代码存储多个拷贝,历史的目的。

由于升级,现在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的链回调。

NB。我不得不使用instance_variable_get@attributes,因为它不具有访问它。您可以在c.filter.options块以及检查find;在上面的例子中,这看起来像:

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

下面是一个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

对于我下面的代码我的模型已经足够了。我不想邮政编码来验证。

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top