ruby-on-rails - relaciones - validaciones rails
Rails-Mejores prácticas: cómo crear relaciones has_one dependientes (6)
¿Podría decirme cuál es la mejor práctica para crear has_one relations?
fe si tengo un modelo de usuario, y debe tener un perfil ...
¿Cómo podría lograr eso?
Una solución sería:
# user.rb
class User << ActiveRecord::Base
after_create :set_default_association
def set_default_association
self.create_profile
end
end
Pero eso no parece muy limpio ... ¿Alguna sugerencia?
Así es como lo hago. No estoy seguro de qué tan estándar es esto, pero funciona muy bien y es flojo en cuanto a que no crea una sobrecarga adicional a menos que sea necesario construir la nueva asociación (me complace que se corrija al respecto):
def profile_with_auto_build
build_profile unless profile_without_auto_build
profile_without_auto_build
end
alias_method_chain :profile, :auto_build
Esto también significa que la asociación está allí tan pronto como lo necesite. Supongo que la alternativa es enlazar en after_initialize, pero parece agregar bastante sobrecarga, ya que se ejecuta cada vez que se inicializa un objeto y puede haber ocasiones en las que no te importa acceder a la asociación. Parece un desperdicio verificar su existencia.
Hay una joya para esto:
https://github.com/jqr/has_one_autocreate
Parece que es un poco viejo ahora. (no funciona con rails3)
La mejor práctica para crear una relación has_one es utilizar la devolución de llamada ActiveRecord before_create
lugar de after_create
. O utilice una devolución de llamada incluso anterior y trate los problemas (si los hay) del niño que no pasa su propio paso de validación.
Porque:
- con una buena codificación, tiene la oportunidad de mostrar las validaciones del registro secundario al usuario si las validaciones fallan
- es más limpio y cuenta con el respaldo explícito de ActiveRecord: AR completa automáticamente la clave externa en el registro secundario después de guardar el registro principal (en la creación). AR luego guarda el registro secundario como parte de la creación del registro principal.
Cómo hacerlo:
# in your User model...
has_one :profile
before_create :build_default_profile
private
def build_default_profile
# build default profile instance. Will use default params.
# The foreign key to the owning User model is set automatically
build_profile
true # Always return true in callbacks as the normal ''continue'' state
# Assumes that the default_profile can **always** be created.
# or
# Check the validation of the profile. If it is not valid, then
# return false from the callback. Best to use a before_validation
# if doing this. View code should check the errors of the child.
# Or add the child''s errors to the User model''s error array of the :base
# error item
end
Probablemente no sea la solución más limpia, pero ya teníamos una base de datos con medio millón de registros, algunos de los cuales ya tenían el modelo ''Perfil'' creado y otros no. Fuimos con este enfoque, que garantiza que un modelo de perfil está presente en cualquier punto, sin necesidad de pasar y generar retroactivamente todos los modelos de perfil.
alias_method :db_profile, :profile
def profile
self.profile = Profile.create(:user => self) if self.db_profile.nil?
self.db_profile
end
Si esta es una nueva asociación en una gran base de datos existente, administraré la transición de esta manera:
class User << ActiveRecord::Base
has_one :profile
before_create :build_associations
def profile
super || build_profile(avatar: "anon.jpg")
end
private
def build_associations
profile || true
end
end
para que los registros de usuario existentes ganen un perfil cuando se le solicite y se creen nuevos con él. Esto también coloca los atributos predeterminados en un lugar y funciona correctamente con accepts_nested_attributes_for en Rails 4 en adelante.
Su solución es definitivamente una forma decente de hacerlo (al menos hasta que crezca), pero puede simplificarla:
# user.rb
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
end