إزالة أو تجاوز التحقق من صحة ActivereCord التي تضاف إليها الطبقة الفائقة أو mixin
-
22-09-2019 - |
سؤال
أنا أستخدم إزالة المصادقة في تطبيق Rails الخاص بي. ال Clearance::User
يضيف Mixin بعض عمليات التحقق من صحة إلى User
النموذج ، ولكن هناك واحد من هؤلاء أود إزالته أو تجاوزه. ما هي أفضل طريقة للقيام بذلك؟
التحقق من الصحة هو
validates_uniqueness_of :email, :case_sensitive => false
وهو في حد ذاته ليس سيئًا ، لكنني سأحتاج إلى إضافة :scope => :account_id
. المشكلة هي أنه إذا أضفت هذا إلى بلدي User
نموذج
validates_uniqueness_of :email, :scope => :account_id
انا حصلت على حد سواء عمليات التحقق ، وتضيف التخليص واحد أكثر تقييدًا من لي ، لذلك ليس لي تأثير. أحتاج إلى التأكد من أن لي يدير فقط. كيف أقوم بهذا العمل؟
المحلول 2
انتهى بي الأمر "حل" المشكلة مع الاختراق التالي:
- ابحث عن خطأ في
:email
سمة من النوع:taken
- تحقق مما إذا كان البريد الإلكتروني فريدًا لهذا الحساب (وهو التحقق الذي أردت القيام به)
- قم بإزالة الخطأ إذا كان البريد الإلكتروني فريدًا لهذا الحساب.
يبدو معقولًا حتى تقرأ الكود واكتشف كيف أقوم بإزالة خطأ. 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
إذا كان أي شخص يعرف طريقة أفضل ، فسأكون ممتنًا جدًا.
نصائح أخرى
كنت أتخلى عن الأحجار الكريمة وأضيف شيكًا بسيطًا ، يمكن بعد ذلك تجاوزه. مثالي يستخدم مصدر قلق.
الاهتمام:
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
لقد واجهت هذه المشكلة مؤخرًا ، وبعد أن لم تعطيني Google الإجابات بسرعة كافية ، وجدت حلاً غير مألوف ولكن لا يزال غير مثالي لهذه المشكلة. الآن لن يعمل هذا بالضرورة في حالتك حيث يبدو أنك تستخدم فصولًا فائقة موجودة مسبقًا ، لكن بالنسبة لي كان هذا الكود الخاص بي ، لذا فقد استخدمت للتو: إذا كان بارام مع نوع تحقق من الفئة الفائقة.
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
في حالة الطبقات الفرعية متعددة البديل
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_eval
و clear_validators!
من AciveRecord::Base
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
في مُهيئ؟
الأخطاء. delete (مفتاح) يزيل جميع الأخطاء لسمة وأريد فقط إزالة نوع محدد من الخطأ الذي ينتمي إلى سمة. يمكن إضافة هذه الطريقة التالية إلى أي نموذج.
إرجاع الرسالة إذا تمت إزالتها ، على خلاف ذلك. يتم تعديل هياكل البيانات الداخلية بحيث يجب أن تعمل جميع الطرق الأخرى كما هو متوقع بعد إزالة الخطأ.
صدر تحت ترخيص معهد ماساتشوستس للتكنولوجيا
طريقة لإزالة الخطأ من النموذج بعد تشغيل عمليات التحقق.
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 و Spree 2.4.11.beta ، بعد ترقية فورة من 2.1.4. يخزن الكود لدينا نسخ متعددة من Spree::Variant
S في طاولة واحدة ، لأغراض تاريخية.
منذ الترقية ، الجوهرة الآن 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
بالنسبة لي على الرمز الناري أدناه كان كافيًا. لا أريد التحقق من صحة zipcode.
after_validation :remove_nonrequired
def remove_nonrequired
errors.messages.delete(:zipcode)
end