2012-08-03 9 views
8

ActiveRecord wydaje się nie rozumieć, że z uwagi na zestaw parametrów dla istniejącego rekordu z zagnieżdżonymi atrybutami, może utworzyć nowy zagnieżdżony rekord, który sam ma zagnieżdżony istniejący rekord. (Drzewo relacji: Existing -> New -> Existing)AcitveRecord nie akceptuje_nested_attributes_for nowych powiązanych rekordów z zagnieżdżonymi istniejącymi rekordami

Czy to błąd, czy coś mi brakuje?

Pozwól, że pokażę Ci prosty przykład.

Oto moje modele:

class User < ActiveRecord::Base 
    has_many :posts 
    attr_accessible :name, :posts_attributes 
    accepts_nested_attributes_for :posts 
end 

class Post < ActiveRecord::Base 
    belongs_to :group 
    belongs_to :user 
    attr_accessible :content, :title, :group_attributes 
    accepts_nested_attributes_for :group 
end 

class Group < ActiveRecord::Base 
    has_many :posts 
    attr_accessible :name 
end 

Zrobiłem jeden rekord w każdej tabeli, a związane z nimi odpowiednio, więc każdy stół ma zapis w nim ze związkiem id=1 --this jest znana. Teraz, gdy mam istniejący użytkownik, nowy post, a istniejące grupy i próby aktualizacji tego rekordu przy użyciu accepts_nested_attributes_for, nie podoba:

1.9.3-p125 :044 > params 
{ 
        :id => 1, 
       :name => "Billy", 
    :posts_attributes => [ 
     [0] { 
          :title => "Title", 
         :content => "Some magnificent content for you!", 
      :group_attributes => { 
        :id => 1, 
       :name => "Group 1" 
      } 
     } 
    ] 
} 
1.9.3-p125 :045 > u 
#<User:0x00000002f7f380> { 
      :id => 1, 
      :name => "Billy", 
    :created_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00, 
    :updated_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00 
} 
1.9.3-p125 :046 > u.update_attributes params 
    (0.1ms) begin transaction 
    (0.1ms) rollback transaction 
ActiveRecord::RecordNotFound: Couldn't find Group with ID=1 for Post with ID= 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:462:in `raise_nested_attributes_record_not_found' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:332:in `assign_nested_attributes_for_one_to_one_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `group_attributes=' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:94:in `block in assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/base.rb:498:in `initialize' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `new' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `build_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/association.rb:233:in `build_record' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/collection_association.rb:112:in `build' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:405:in `block in assign_nested_attributes_for_collection_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `assign_nested_attributes_for_collection_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `posts_attributes=' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:85:in `block in assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:216:in `block in update_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:208:in `transaction' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:293:in `with_transaction_returning_status' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:215:in `update_attributes' 
    from (irb):15 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>' 
    from script/rails:6:in `require' 
    from script/rails:6:in `<main>'1.9.3-p125 :047 > 

ona myśli, że nie może znaleźć grupę (ze znanym ID), związane z nowym postem. Działa po usunięciu identyfikatora z group_attributes (ale tworzy nowy rekord grupy). Działa, gdy podam posts_attributes identyfikator i usuń identyfikator z group_attributes (i ponownie tworzy nową grupę). Działa również, gdy wszystkie mają identyfikatory.

Związek działa:

1.9.3-p125 :059 > p = Post.new({ group_attributes: { name: 'Testing' } }) 
#<Post:0x00000004212380> { 
      :id => nil, 
     :title => nil, 
     :content => nil, 
     :group_id => nil, 
     :user_id => nil, 
    :created_at => nil, 
    :updated_at => nil 
} 
1.9.3-p125 :060 > p.group 
[ 
    [0] #<Group:0x00000004211868> { 
       :id => nil, 
       :name => "Testing", 
     :created_at => nil, 
     :updated_at => nil 
    } 
] 

również całkowicie działa podczas korzystania posts_attributes i group_attributes podczas User stworzenia, jeśli wszystkie rekordy są nowe.

Czy nie powinien działać nadal w pierwszym przykładzie? ActiveRecord powinien być wystarczająco inteligentny, aby to zrozumieć ...!

+0

Brakuje 'has_many: comments' w modelu użytkownika. – jordanpg

+0

oops, problem nadal pozostaje. :) Uproszczę przykład. – wulftone

+0

długie pytanie, ale w jaki sposób nowy rekord ma istniejące powiązanie? – pjammer

Odpowiedz

4

Oto, co moim zdaniem dzieje się: przekazujesz identyfikator grupy, wskazując ActiveRecord, że grupa istnieje. ActiveRecord próbuje znaleźć tę grupę, aby zaktualizować ją o inne dane, które posiadasz w group_attributes. Ponieważ robisz to wewnątrz z post_attributes, ActiveRecord próbuje znaleźć tę grupę poprzez stowarzyszenia między postu a grupą. Oznacza to, że ActiveRecord najpierw szuka skojarzonej grupy - gdzie id = post.group_id - wtedy z tego wyniku szuka tej o ID = 1. Może to wydawać się trochę dziwne i niezdarne dla relacji z rodzicami, tak jak w twoim przypadku, ale Jestem pewien, że widzisz, że jest to użyteczne zachowanie podczas przechodzenia w drugą stronę, gdzie zagnieżdżone atrybuty reprezentują jedno lub więcej potencjalnie wielu dzieci.

Jednak twój obiekt postu, utworzony z danych w post_attributes, nie jest jeszcze powiązany z grupą - post.group_id jest zerowy. Tak więc, gdy ActiveRecord robi to pierwsze wyszukiwanie, aby uzyskać skojarzoną grupę, pojawia się puste. Odpowiednio, nie znajdzie grupy o ID = 1 w (puste) wyniki. Technicznie rzecz biorąc, zapis jest tam, ale nie ma go pod numerem pod względem powiązania ze stanowiskiem.

Możesz to udowodnić, dodając parametr group_id => 1 w atrybutach post_.Wierzę, że jeśli to zrobisz, ActiveRecord znajdzie grupę przez asocjację, a następnie dokona sub-select grupy o ID = 1 z wyników, z powodzeniem, a następnie zaktualizuje tę grupę o dodatkowe dane w atrybutach group_attributes.

Należy również pamiętać, że jedynym powodem, dla którego jest dołączanie zagnieżdżonych atrybutów dla grupy wewnątrz postu, tak jak to robisz, jest to, że użytkownik może aktualizować nazwę grupy w tym samym czasie, gdy tworzy nowy wpis. Jeśli chcesz tylko połączyć nowy wpis z istniejącą grupą, musisz dodać parametr group_id do post_attributes i możesz pozbyć się atrybutów group_attributes.

+0

Wydaje mi się to logiczne, ale ten scenariusz już dawno minął. :) Pamiętam, że chciałbym jednocześnie zaktualizować atrybuty skojarzenia i rekordu głównego (nie używałem tych dokładnych tabel użytkowników, postów i grup, ale była to podobna struktura). Uważam, że potrzeba takiego działania została skierowana na powrót do przyszłego wdrożenia, a następnie o nim zapomniano. Może wrócić, by nas prześladować! Dzięki za odpowiedź. Na pewno się do tego odwołam, gdy znów się na to natknę. – wulftone

+0

Próbuję go ponownie, otrzymuję nowe "Grupy" utworzone, nawet jeśli mam 'group_id' w' Postu' - chociaż nie powoduje to błędu. Po prostu ignoruje 'group_id' i tworzy go tak czy inaczej. Nadal nie wydaje się możliwe do zrobienia. – wulftone

+0

Cholera. Naprawdę myślałem, że mam tego przybitego. Poświęcę trochę czasu na zabawę ze scenariuszem w ten weekend i zobaczę, co mogę wymyślić, to utknęło teraz we mnie. ;) – Yardboy

Powiązane problemy