Как отключить проверку и обратные вызовы в производной модели RTI?
Учитывая модель
class BaseModel < ActiveRecord::Base
validates_presence_of :parent_id
before_save :frobnicate_widgets
end
и производная модель (базовая таблица базы данных имеет поле type
- это простые рельсы STI)
class DerivedModel < BaseModel
end
DerivedModel
будет в хорошем стиле OO наследовать все поведение от BaseModel
, включая validates_presence_of :parent_id
. Я хотел бы отключить валидацию для DerivedModel
и запретить запуск методов обратного вызова, предпочтительно без изменения или другого взлома BaseModel
Какой самый простой и надежный способ сделать это?
Ответы
Ответ 1
Мне нравится использовать следующий шаблон:
class Parent < ActiveRecord::Base
validate_uniqueness_of :column_name, :if => :validate_uniqueness_of_column_name?
def validate_uniqueness_of_column_name?
true
end
end
class Child < Parent
def validate_uniqueness_of_column_name?
false
end
end
Было бы неплохо, если бы рельсы предоставили метод skip_validation, чтобы обойти это, но этот шаблон работает и хорошо справляется с сложными взаимодействиями.
Ответ 2
Как вариант ответа от @Jacob Rothstein, вы можете создать метод в родительском:
class Parent < ActiveRecord::Base
validate_uniqueness_of :column_name, :unless => :child?
def child?
is_a? Child
end
end
class Child < Parent
end
Преимущество такого подхода заключается в том, что вам не нужно создавать несколько методов для каждого имени столбца, которое необходимо отключить для проверки в классе Child.
Ответ 3
От толкания в источнике (в настоящее время я на рельсах 1.2.6) обратные вызовы относительно просты.
Оказывается, что методы before_validation_on_create
, before_save
и т.д., если не вызывать с любыми аргументами, возвращают массив, который содержит все текущие обратные вызовы, назначенные этому "сайту обратного вызова"
Чтобы очистить before_save, вы можете просто сделать
before_save.clear
и, похоже, работает
Ответ 4
Более чистый способ:
class Parent < ActiveRecord::Base
validate :column_name, uniqueness: true, if: 'self.class == Parent'
end
class Child < Parent
end
Или вы можете использовать его также следующим образом:
class Parent < ActiveRecord::Base
validate :column_name, uniqueness: true, if: :check_base
private
def check_base
self.class == Parent
end
end
class Child < Parent
end
Таким образом, проверка уникальности выполняется, если класс экземпляра модели Parent
.
- Класс экземпляра
Child
равен Child
и отличается от Parent
.
- Класс экземпляра
Parent
равен Parent
и совпадает с Parent
.
Ответ 5
Снова вырываясь в исходном коде, кажется, что проверки можно запускать либо при каждом сохранении, либо обновлять/создавать только. Это сопоставляется с
:validate
= > все сохраняет
:validate_on_create
= > творения только
:validate_on_update
= > только обновления
Чтобы очистить их, вы можете использовать write_inheritable_attribute
, например:
write_inheritable_attribute :validate, nil
Ответ 6
Вот небольшая вариация RubyDev, которую я использовал в mongoid 3.
class Parent
include Mongoid::Document
validates :column_name , uniqueness: true, unless: Proc.new {|r| r._type == "Child"}
end
class Child < Parent
end
До сих пор он работал очень хорошо.
Ответ 7
С помощью rails 3.0 вы также можете получить доступ к методу validators
для управления, чтобы получить список всех проверок. Однако вы не можете манипулировать набором проверок через этот массив.
Как минимум, с рельсов 5.0 вы, похоже, можете манипулировать методом _validators
(недокументированный).
Используя этот метод, вы можете изменить валидации в подклассе, например, например:
class Child < Parent
# add additional conditions if necessary
_validators.reject! { |attribute, _| attribute == :parent_id }
end
В то время как это использует недокументированный метод, имеет преимущество не требовать, чтобы суперкласс знал что-либо о реализации ребенка.