ruby-on-rails - formularios - rails asociaciones
¿Cómo puedo desactivar una validación y devoluciones de llamada en un modelo derivado de STI de rieles? (7)
Al hurgar en la fuente (actualmente estoy en Rails 1.2.6), las devoluciones de llamada son relativamente sencillas.
Resulta que los before_validation_on_create
, before_save
etc., si no se invocan con ningún argumento, devolverán la matriz que contiene todas las devoluciones de llamada actuales asignadas a ese ''sitio de devolución de llamada''
Para borrar los before_save, simplemente puede hacer
before_save.clear
Y parece funcionar
Dado un modelo
class BaseModel < ActiveRecord::Base
validates_presence_of :parent_id
before_save :frobnicate_widgets
end
y un modelo derivado (la tabla de base de datos subyacente tiene un campo type
- esto es STI de rails simples)
class DerivedModel < BaseModel
end
DerivedModel
heredará todos los comportamientos de BaseModel
, incluida la validates_presence_of :parent_id
. Me gustaría desactivar la validación de DerivedModel
y evitar que los métodos de devolución de llamada se DerivedModel
, preferiblemente sin modificar o romper el BaseModel
¿Cuál es la forma más fácil y más robusta de hacer esto?
Al volver a hurgar en la fuente, parece que las validaciones se pueden ejecutar en cada operación de guardado o solo en las actualizaciones / creaciones. Esto se asigna a
:validate
=> all guarda
:validate_on_create
=> creaciones solamente
:validate_on_update
=> updates only
Para borrarlos, puedes usar write_inheritable_attribute
, así:
write_inheritable_attribute :validate, nil
Me gusta usar el siguiente patrón:
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
Sería bueno si Rails proporcionara un método skip_validation para evitar esto, pero este patrón funciona y maneja bien las interacciones complejas.
Como una variación de la respuesta de @Jacob Rothstein, puedes crear un método en el padre:
class Parent < ActiveRecord::Base
validate_uniqueness_of :column_name, :unless => :child?
def child?
is_a? Child
end
end
class Child < Parent
end
El beneficio de este enfoque es que no necesita crear múltiples métodos para cada nombre de columna para el que necesita desactivar la validación en la clase Child.
Aquí hay una pequeña variación de RubyDev que he estado usando en mongoid 3 .
class Parent
include Mongoid::Document
validates :column_name , uniqueness: true, unless: Proc.new {|r| r._type == "Child"}
end
class Child < Parent
end
Hasta ahora ha estado funcionando bastante bien para mí.
Una forma más limpia es esta:
class Parent < ActiveRecord::Base
validate :column_name, uniqueness: true, if: ''self.class == Parent''
end
class Child < Parent
end
O puede usarlo también de esta manera:
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
Entonces, la validación de unicidad se hace si la clase de instancia del modelo es Parent
.
- La clase de instancia de
Child
isChild
y difiere deParent
. - La clase de instancia de
Parent
esParent
y es igual aParent
.
Desde rails 3.0 también puede acceder al método de la clase de validators
para manipular y obtener una lista de todas las validaciones. Sin embargo, no puede manipular el conjunto de validaciones a través de esta matriz.
Al menos a partir de Rails 5.0, sin embargo, parece ser capaz de manipular el _validators
(no documentado).
Usando este método puedes modificar las validaciones en la subclase como por ejemplo:
class Child < Parent
# add additional conditions if necessary
_validators.reject! { |attribute, _| attribute == :parent_id }
end
Si bien esto utiliza un método no documentado, tiene el beneficio de no requerir que la superclase sepa nada sobre la implementación del niño.