Как я могу отключить валидацию и обратные вызовы в модели, основанной на STI рельсов? - PullRequest
18 голосов
/ 11 ноября 2008

Учитывая модель

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

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

Ответы [ 7 ]

42 голосов
/ 28 марта 2009

Мне нравится использовать следующий шаблон:

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

Было бы неплохо, если бы rails предоставил метод skip_validation, чтобы обойти это, но этот шаблон работает и хорошо обрабатывает сложные взаимодействия.

8 голосов
/ 18 апреля 2012

В качестве варианта ответа @Jacob Rothstein вы можете создать метод в parent:

class Parent < ActiveRecord::Base
  validate_uniqueness_of :column_name, :unless => :child?
  def child?
    is_a? Child
  end
end

class Child < Parent
end

Преимущество этого подхода заключается в том, что вам не нужно создавать несколько методов для каждого имени столбца, для которого необходимо отключить проверку в дочернем классе.

3 голосов
/ 11 ноября 2008

С поиском в источнике (я в настоящее время на рельсах 1.2.6), обратные вызовы относительно просты.

Оказывается, что методы before_validation_on_create, before_save и т. Д., Если они не вызваны с какими-либо аргументами, вернут массив, который содержит все текущие обратные вызовы, назначенные этому «сайту обратного вызова»

Чтобы очистить ранее сохраненные, вы можете просто сделать

before_save.clear

и похоже на работу

1 голос
/ 14 сентября 2017

Начиная с rails 3.0, вы также можете получить доступ к validators методу класса , чтобы манипулировать, чтобы получить список всех проверок. Однако вы не можете манипулировать набором проверок через этот массив.

По крайней мере, с рельсов 5.0 вы, похоже, способны манипулировать методом _validators (недокументированным).

Используя этот метод, вы можете изменить валидации в подклассе, например, например:

class Child < Parent
  # add additional conditions if necessary
  _validators.reject! { |attribute, _| attribute == :parent_id } 
end

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

1 голос
/ 25 февраля 2015

Более чистый способ:

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.

  1. Экземпляр класса Child равен Child и отличается от Parent.
  2. Экземпляр класса Parent равен Parent и совпадает с Parent.
0 голосов
/ 30 января 2013

Вот небольшое изменение 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

До сих пор это работало довольно хорошо для меня.

0 голосов
/ 11 ноября 2008

Опять копаясь в источнике, кажется, что проверки могут выполняться либо при каждом сохранении, либо только при обновлении / создании. Это соответствует

:validate => все сохранения
:validate_on_create => только творения
:validate_on_update => только обновления

Чтобы очистить их, вы можете использовать write_inheritable_attribute, например:

write_inheritable_attribute :validate, nil
...