2012-03-15 12 views
9

en un proyecto de Ruby on Rails Estoy intentando acceder a objetos de asociación en un ActiveRecord antes de guardar todo en la base de datos.Ruby on Rails has_many a través de objetos de asociación antes de guardar

class Purchase < ActiveRecord::Base 

    has_many :purchase_items, dependent: :destroy 
    has_many :items, through: :purchase_items 

    validate :item_validation 

    def item_ids=(ids) 
    ids.each do |item_id| 
     purchase_items.build(item_id: item_id) 
    end 
    end 

    private 

    def item_validation 
    items.each do |item| 
     ## Lookup something with the item 
     if item.check_something 
     errors.add :base, "Error message" 
     end 
    end 
    end 

end 

Si construyo mi objeto de esta manera: purchase = Purchase.new(item_ids: [1, 2, 3]) y tratar de guardarlo en el método item_validation no tiene la colección de artículos todavía poblada, por lo que a pesar de que los artículos se han establecido establecer que no recibe una posibilidad de llamar al método check_something en cualquiera de ellos.

¿Es posible acceder a la colección de elementos antes de que mi modelo de compra y modelos de asociación se mantengan para que pueda ejecutar las validaciones en su contra?

Si cambio mi método item_validation ser:

def item_validation 
    purchase_items.each do |purchase_item| 
    item = purchase_item.item 
    ## Lookup something with the item 
    if item.something 
     errors.add :base, "Error message" 
    end 
    end 
end 

parece que funciona de la manera que yo quiero que, sin embargo me resulta difícil de creer que no hay manera de acceder directamente a la colección de artículos con Rieles antes de mi compra y los registros asociados se guardan en la base de datos.

Respuesta

0

¿Tiene documentación que indique que purchase = Purchase.new(item_ids: [1, 2, 3]) hace lo que está esperando?

Para mí, parece que está configurando el atributo no de base de datos 'item_ids' a una matriz (es decir, no crea una asociación).

Su modelo de compra ni siquiera debería tener columnas de clave externa para establecer directamente. En su lugar, hay entradas en la tabla purchase_items que tienen un purchase_id y item_id. Para crear un enlace entre su compra y los tres elementos, necesita crear tres entradas en la tabla de unión.

¿Qué pasa si usted acaba de hacer esto en vez ?:

purchase = Purchase.new 
purchase.items = Item.find([1,2,3]) 
1

Trate de añadir el argumento inverse_of: en el has_many y belongs_to definiciones. El argumento inverse_of es el nombre de la relación en el otro modelo, por ejemplo:

class Post < ActiveRecord::Base 
    has_many :comments, inverse_of: :post 
end 

class Comment < ActiveRecord::Base 
    belongs_to :post, inverse_of: :comments 
end 

No olvide añadirlo también en las otras clases, como PurchaseItem y artículo

creo que sirve

1

Elimine su propio método item_ids=; los rieles generan uno para usted (consulte collection_singular_ids=ids). Esto ya podría resolver su problema.

class Purchase < ActiveRecord::Base 

    has_many :purchase_items, dependent: :destroy 
    has_many :items, through: :purchase_items 

    validate :item_validation 

    private 

    def item_validation 
    items.each do |item| 
     ## Lookup something with the item 
     if item.check_something 
     errors.add :base, "Error message" 
     end 
    end 
    end 

end 

La segunda cosa que viene en mi mente a mirar tu código: Mueva la validación a la clase Item. Por lo tanto:

class Purchase < ActiveRecord::Base 
    has_many :purchase_items, dependent: :destroy 
    has_many :items, through: :purchase_items 
end 

class Item < ActiveRecord::Base 
    has_many :purchase_items 
    has_many :purchases, through: :purchase_items 

    validate :item_validation 

    private 

    def item_validation 
     if check_something 
     errors.add :base, "Error message" 
     end 
    end 
end 

Su registro Purchase también será válida si uno de los Item s no es válido.

0

Supongo que no puede acceder a ellos porque id de Purchase no está disponible antes de guardar el registro. Pero como usted menciona tiene acceso al primer nivel de asociación purchase_items, por lo que puede extraer todos los identificadores y pasarlos en where para Item:

items = Item.where(purchase_item_id: purchase_items.map(&:id)) 
Cuestiones relacionadas