validar validaciones rails formulario asociaciones ruby-on-rails ruby

ruby-on-rails - formulario - validaciones rails



¿Cómo se puede validar la presencia de un miembro de la asociación con Rails? (6)

En lugar de validar la presencia de la identificación del artículo, se podría validar la presencia del artículo.

validates_presence_of :article

Entonces cuando estés creando tu comentario:

article.comments.build :article => article

Digamos que tengo una aplicación básica de Rails con una relación básica de uno a varios donde cada comentario pertenece a un artículo:

$ rails blog $ cd blog $ script/generate model article name:string $ script/generate model comment article:belongs_to body:text

Ahora agrego el código para crear las asociaciones, pero también quiero asegurarme de que cuando creo un comentario, siempre tenga un artículo:

class Article < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :article validates_presence_of :article_id end

Entonces ahora digamos que me gustaría crear un artículo con un comentario de una vez:

$ rake db:migrate $ script/console

Si haces esto:

>> article = Article.new => #<Article id: nil, name: nil, created_at: nil, updated_at: nil> >> article.comments.build => #<Comment id: nil, article_id: nil, body: nil, created_at: nil, updated_at: nil> >> article.save!

Obtendrás este error:

ActiveRecord::RecordInvalid: Validation failed: Comments is invalid

Lo que tiene sentido, porque el comentario aún no tiene page_id.

>> article.comments.first.errors.on(:article_id) => "can''t be blank"

Entonces, si comment.rb validates_presence_of :article_id de comment.rb , entonces podría guardar, pero eso también le permitiría crear comentarios sin un ID de artículo. ¿Cuál es la forma típica de manejar esto?

ACTUALIZACIÓN : Basado en la sugerencia de Nicholas, aquí hay una implementación de save_with_comments que funciona pero es fea:

def save_with_comments save_with_comments! rescue false end def save_with_comments! transaction do comments = self.comments.dup self.comments = [] save! comments.each do |c| c.article = self c.save! end end true end

No estoy seguro de que quiera agregar algo como esto para cada asociación de uno a muchos. Probablemente Andy tenga razón, ya que es mejor evitar tratar de guardar en cascada y usar la solución de atributos anidados. Dejaré esto abierto por un tiempo para ver si alguien tiene alguna otra sugerencia.


Estás en lo correcto. El artículo necesita una identificación antes de que esta validación funcione. Una forma de evitar esto es guardar el artículo, así:

>> article = Article.new => #<Article id: nil, name: nil, created_at: nil, updated_at: nil> >> article.save! => true >> article.comments.build => #<Comment id: nil, article_id: 2, body: nil, created_at: nil, updated_at: nil> >> article.save! => true

Si está creando un nuevo artículo con un comentario en un método o acción, le recomendaría crear el artículo y guardarlo, luego crear el comentario, pero envolver todo dentro de un bloque Article.transaction para que no finalice. con cualquier artículo extra.


Esto falla porque no hay un mapa de identidad en Rails 2.3 o 3.0. Se puede arreglar manualmente cosiéndolos juntos.

a = Article.new c = a.comments.build c.article = a a.save!

Esto es horrible, y lo que el mapa de identidad en 3.1 ayudará a solucionar (además de las mejoras de rendimiento). c.article y a.comments.first.article son objetos diferentes sin un mapa de identidad.


Si está utilizando Rails 2.3, está utilizando el nuevo modelo de modelo anidado. He notado las mismas fallas con validates_presence_of que usted señaló, o si :null => false fue especificado en la migración para ese campo.

Si su intención es crear modelos anidados, debe agregar accepts_nested_attributes_for :comments to article.rb . Esto le permitiría:

a = Article.new a.comments_attributes = [{:body => "test"}] a.save! # creates a new Article and a new Comment with a body of "test"

Lo que tienes es la forma en que debería funcionar para mí, pero veo que no es así con Rails 2.3.2. before_create un before_create en un comentario usando su código y no se proporciona article_id través de la compilación (es nula), por lo que esto no va a funcionar. Necesitaría guardar el artículo primero como lo señaló Nicholas, o eliminar la validación.


Solucioné este problema agregando esta línea de seguimiento a mi _comment.html.erb:

"NUEVO" si form.object.new_record? %>

Ahora, la validación funciona en forma independiente, y también en forma múltiple.


También he estado investigando este tema y aquí está mi resumen:

La causa raíz por la que esto no funciona OOTB (al menos cuando se usa validates_presence_of :article y no validates_presence_of :article_id ) es el hecho de que rails no usa un mapa de identidad internamente y, por lo tanto, no sabrá por sí mismo que article.comments[x].article == article

He encontrado tres soluciones para que funcione con un poco de esfuerzo:

  1. Guarde el artículo antes de crear los comentarios (los rieles pasarán automáticamente la identificación del artículo que se generó durante el guardado de cada comentario creado recientemente; vea la respuesta de Nicholas Hubbard)
  2. Establezca explícitamente el artículo en el comentario después de crearlo (vea la respuesta de W. Andrew Loe III)
  3. Utilice inverse_of:

    class Article < ActiveRecord::Base has_many :comments, :inverse_of => :article end

Esta última solución fue un bot aún mencionado en este artículo, pero parece ser la solución de solución rápida de Rails por la falta de un mapa de identidad. También me parece la menos intrusiva de las tres.