Подтвердить что ты не робот

Как отключить проверку и обратные вызовы в производной модели 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

Какой самый простой и надежный способ сделать это?

4b9b3361

Ответ 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

В то время как это использует недокументированный метод, имеет преимущество не требовать, чтобы суперкласс знал что-либо о реализации ребенка.