Как я могу отключить проверку и обратные вызовы в производной модели rails STI?

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

class BaseModel < ActiveRecord::Base
  validates_presence_of :parent_id
  before_save :frobnicate_widgets
end

и производная модель (базовая таблица базы данных имеет поле type — это простая рельсовая STI)

class DerivedModel < BaseModel
end

DerivedModel наследует все поведение от BaseModel, включая поведение validates_presence_of :parent_id. Я хотел бы отключить проверку для DerivedModel и предотвратить срабатывание методов обратного вызова, желательно без изменения или иного нарушения BaseModel

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


person Orion Edwards    schedule 10.11.2008    source источник


Ответы (7)


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

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, чтобы обойти это, но этот шаблон работает и хорошо обрабатывает сложные взаимодействия.

person Community    schedule 28.03.2009

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

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

class Child < Parent
end

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

person Chandresh Pant    schedule 18.04.2012
comment
Короче: validate_uniqueness_of :column_name, :unless => "is_a? Child" - person Sidhannowe; 16.10.2019

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

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

Чтобы очистить файлы before_save, вы можете просто сделать

before_save.clear

и вроде работает

person Orion Edwards    schedule 10.11.2008

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

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.
person phlegx    schedule 25.02.2015
comment
Эй .. Это отличный способ, который вы предложили. Я использую класс из драгоценного камня, поэтому я не могу изменить родительский класс и функцию check_base. Можете ли вы предложить мне способ, где я могу добавить какой-то пропуск проверки в дочернем классе. - person VoodooChild92; 27.08.2015

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

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

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

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

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

person ulferts    schedule 14.09.2017

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

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

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

write_inheritable_attribute :validate, nil
person Orion Edwards    schedule 10.11.2008

Вот небольшой вариант 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

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

person hadees    schedule 30.01.2013
comment
Осведомленность о существующем дочернем классе в родительском — не лучший подход. Я пытаюсь справиться с этим другим способом. - person Leszek Andrukanis; 20.11.2013