2010-04-09 16 views
20

Ho un paio di modelli in questo modoRuby on Rails - attributi nidificati: Come faccio ad accedere al modello di genitore da bambino modello

class Bill < ActiveRecord::Base 
    has_many :bill_items 
    belongs_to :store 

    accepts_nested_attributes_for :bill_items 
end 

class BillItem <ActiveRecord::Base 
    belongs_to :product 
    belongs_to :bill 

    validate :has_enough_stock 

    def has_enough_stock 
    stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity 
    errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity 
    end 
end 

È possibile che convalida così evidentemente non funziona perché quando sto leggendo Gli attributi bill_item.bill_id o bill_item.bill non sono disponibili prima di essere salvati.

Quindi, come faccio a fare qualcosa del genere?

+0

Ho risolto questo problema aggiungendo una chiamata all'associazione,: before_add =>: set_nest – TMaYaD

risposta

18

Ecco come l'ho risolto alla fine; impostando genitore su richiamata

has_many :bill_items, :before_add => :set_nest 

private 
    def set_nest(bill_item) 
    bill_item.bill ||= self 
    end 
+1

Sì! Mi ha infastidito ultimamente. Vorrei che fosse automatico, si poteva scrivere una versione più generica se le funzioni di callback venivano eseguite contro il proxy dell'associazione (come le estensioni di associazione) invece del proprietario dell'associazione. – ilpoldo

0

Sì, questo tipo di problema può essere fastidioso. Si potrebbe provare ad aggiungere un attributo virtuale al modello dell'oggetto Bill in questo modo:

class BillItem <ActiveRecord::Base 
    belongs_to :product 
    belongs_to :bill 

    attr_accessible :store_id 

    validate :has_enough_stock 

    def has_enough_stock 
    stock_available = Inventory.product_is(self.product).store_is(load_bill_store).one.quantity 
    errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity 
    end 

    private 

    def load_bill_store 
    Store.find_by_id(self.store_id) 
    end 
end 

E poi a suo avviso si potrebbe aggiungere un campo nascosto come questo:

<%= bill_item.hidden_field :store_id, :value => store_id %> 

Questo non è stato test, ma potrebbe funzionare. Potresti non trovare desiderabile avere store_id nell'html ma potrebbe non essere una preoccupazione. Fammi sapere se questo aiuta.

1

Il bill_item.bill dovrebbe essere disponibile, si potrebbe provare a fare un aumento self.bill.inspect per vedere se è lì o no, ma penso che il problema sia altrove.

+0

Anche se non è disponibile, l'OP potrebbe semplicemente aggiungere un callback before_validation per impostarlo. Il BillItems non dovrebbe aver bisogno di conoscere i propri ID o l'ID del genitore Bill da convalidare. – hornairs

1

I "fisso" impostando madre in uno callback:

class Bill < ActiveRecord::Base 
    has_many :bill_items, :dependent => :destroy, :before_add => :set_nest 
    belongs_to :store 

    accepts_nested_attributes_for :bill_items 

    def set_nest(item) 
    item.bill ||= self 
    end 
end 

class BillItem <ActiveRecord::Base 
    belongs_to :product 
    belongs_to :bill 

    validate :has_enough_stock 

    def has_enough_stock 
     stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity 
    errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity 
    end 
end 

Il metodo set_nest ha fatto il trucco. Vorrei che fosse standard con accept_nested_attributes_for.

7

In Rails 4 (non prova sulle versioni precedenti), è possibile accedere al modello genitore impostando l'opzione inverse_of su has_many o has_one:

class Bill < ActiveRecord::Base 
    has_many :bill_items, inverse_of: :bill 
    belongs_to :store 

    accepts_nested_attributes_for :bill_items 
end 

Documentazione: Bi-directional associations

+0

Questa è la vera risposta per i binari 4 – Abs

Problemi correlati