2009-07-30 13 views
22

Zaktualizowałem do Rails 2.3.3 (z wersji 2.1.x) i próbuję znaleźć metodę accepts_nested_attributes_for. Mogę użyć tej metody do aktualizacji istniejących zagnieżdżonych obiektów, ale nie mogę jej użyć do utworzenia nowych zagnieżdżonych obiektów. Biorąc pod uwagę wymyślony przykład:Jak utworzyć zagnieżdżone obiekty za pomocą accepts_nested_attributes_for

class Product < ActiveRecord::Base 
    has_many :notes 
    accepts_nested_attributes_for :notes 
end 

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id, :body 
end 

Gdy próbuję utworzyć nowy Product, z zagnieżdżonego Note, co następuje:

params = {:name => 'Test', :notes_attributes => {'0' => {'body' => 'Body'}}} 
p = Product.new(params) 
p.save! 

zawiedzie To walidacji z komunikatem:

ActiveRecord::RecordInvalid: Validation failed: Notes product can't be blank 

Rozumiem, dlaczego tak się dzieje - dzieje się tak z powodu klasy validates_presence_of :product_id na klasie Note, a ponieważ w momencie zapisywania nowego rekordu, Product Obiekt nie ma wartości id. Jednak nie chcę usunąć tego sprawdzania poprawności; Myślę, że niewłaściwe byłoby usunięcie go.

Mogę również rozwiązać problem, najpierw ręcznie tworząc Product, a następnie dodając Note, ale to pokonuje prostotę accepts_nested_attributes_for.

Czy istnieje standardowy sposób Rails tworzenia obiektów zagnieżdżonych na nowych rekordach?

Odpowiedz

16

Jest to typowy, zależny od zależności problem. Jest existing LightHouse ticket, który warto sprawdzić.

Spodziewam się, że w Rails 3 ulegnie znacznej poprawie, ale w międzyczasie będziesz musiał obejść ten problem. Jednym z rozwiązań jest ustawienie wirtualnego atrybutu, który ustawisz podczas zagnieżdżania, aby warunkowanie było warunkowe.

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id, :unless => :nested 
    attr_accessor :nested 
end 

A następnie ustawić ten atrybut jako ukryte pole w formularzu.

<%= note_form.hidden_field :nested %> 

To powinno wystarczyć, aby mieć atrybut nested ustawione podczas tworzenia wiadomości poprzez formularz zagnieżdżonego. Bez ograniczeń.

+11

W szyną 3, ten problem jest rozwiązany przez dodaną: inverse_of możliwość HAS_MANY, has_one i belongs_to. Zobacz np. http://www.daokaous.com/rails3.0.0_doc/classes/ActiveRecord/Associations/ClassMethods.html#M001988 w sekcji "Połączenia dwukierunkowe" –

+2

Wybrałem wyłączenie sprawdzania poprawności, gdy: id == zero . Ponieważ to powinno się zdarzyć tylko przy pisaniu nowego, zagnieżdżonego rekordu, mam nadzieję, że będzie bezpieczny. Dziwne, że ta kwestia doprowadziła do końca do wersji 2.3.8. – aceofspades

+3

Funkcja inverse_of wydaje się nie działać, gdy rodzic nadrzędny jeszcze nie istnieje, a zatem nie można przetestować żadnego id. Wygląda na to, że ten zagnieżdżony hack jest jedyną drogą. :( – DGM

3

rozwiązanie Ryana jest rzeczywiście bardzo fajne. Poszedłem i uczyniłem mój kontroler grubszym, aby to zagnieżdżenie nie pojawiło się w widoku. Głównie dlatego, że mój pogląd jest czasami json, więc chcę być w stanie uciec tak mało jak to tylko możliwe.

class Product < ActiveRecord::Base 
    has_many :notes 
    accepts_nested_attributes_for :note 
end 

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id unless :nested 
    attr_accessor :nested 
end 

class ProductController < ApplicationController 

def create 
    if params[:product][:note_attributes] 
     params[:product][:note_attributes].each { |attribute| 
      attribute.merge!({:nested => true}) 
    } 
    end 
    # all the regular create stuff here 
end 
end 
7
+2

bardzo ważne jest, aby potwierdzić obecność "produktu", a nie "product_id" – kuboon

+0

Myślę, że kuboon ma właściwą odpowiedź. "product", a nie "product_id" wraz ze zmianą asocjacji na Product na "has_many: notes, inverse_of:: product" powinny to zrobić. – quainjn

Powiązane problemy