Rails: Używanie build z asocjacją has_one w railsach

143

W tym przykładzie tworzę userbez profile, a później tworzę profiledla tego użytkownika. Próbowałem użyć kompilacji ze has_oneskojarzeniem, ale to wybuchło. Jedynym sposobem, w jaki widzę, że to działa, jest użycie has_many. userMa mieć tylko co najwyżej jeden profile.

Próbowałem tego. Mam:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Ale kiedy robię:

user.build_profile 

Wyskakuje mi błąd:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Czy istnieje sposób w szynach na 0 lub 1 skojarzenie?

espinet
źródło
czego dokładnie próbowałeś czy mógłbyś, proszę, wysłać jakiś kod?
Ju Nogueira,

Odpowiedzi:

359

buildPodpis metoda jest różna dla has_onei has_manystowarzyszenia.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

Składnia kompilacji dla has_manyasocjacji:

user.messages.build

Składnia kompilacji dla has_oneasocjacji:

user.build_profile  # this will work

user.profile.build  # this will throw error

Przeczytaj dokumentacjęhas_one stowarzyszenia, aby uzyskać więcej informacji.

Harish Shetty
źródło
28
Inna składnia has_one zawsze mnie zaskakuje ... cholera!
Galaxy
11
To zabawne, jak najwyżej oceniana i akceptowana odpowiedź tutaj odpowiada na inne pytanie niż to, które zadał OP.
Ajedi32
Przypuszczalnie jeśli użytkownik należał do profilu (co oznacza, że ​​tabela użytkownika ma obcy_klucz profile_id w swojej tabeli), to również budowanie profilu dla użytkownika będzie działało jak wspomniano powyżej, tj. Ale dla nowej akcji tylko user.build_profile do edycji user.build_profile if user.profile.nil? i jeśli chcesz zbudować profil podczas tworzenia użytkownika, wpisz accepts_nested_attributes_for :profileto w Model użytkownika. iw formularzu, który użytkownik jest tworzony, napisz <%= f.simple_fields_for :profile do |p| %>to i kontynuuj.
gorliwość
ale dlaczego to inne zachowanie zostało zachowane dla has_one lub has_many? Przy projektowaniu byłby jakiś powód, myślę i oczekuję.
dociekliwy
@ Ajedi32 odpowiedź pasuje do tytułu pytania, ale nie do treści. Biorąc pod uwagę, że to ( build_<association>) jest dość dziwnym i nieoczekiwanym zachowaniem w Railsach, o wiele więcej ludzi szuka tej odpowiedzi niż odpowiedzi na pytania, jeśli wiesz, o co mi chodzi.
Max Williams
19

Przyjrzyj się dokładnie komunikatowi o błędzie. Mówi ci, że nie masz wymaganej kolumny user_idw tabeli profilu . Ustalenie relacji w modelu to tylko część odpowiedzi.

Musisz także utworzyć migrację, która doda user_idkolumnę do tabeli profili. Railsy tego oczekują, a jeśli tak nie jest, nie możesz uzyskać dostępu do profilu.

Więcej informacji znajdziesz pod tym linkiem:

Podstawy asocjacji

sosborn
źródło
1
Właśnie odkryłem mój problem. Książka, z której się uczę, nie wyjaśniała dobrze tworzenia klucza obcego. Utworzyłem nową migrację, która dodaje klucz obcy do mojego modelu. dzięki.
espinet
Czy za każdym razem musisz samodzielnie tworzyć kolumnę? Pomyślałem, że stało się to automagicznie. Nie wiem, skąd wziął się ten pomysł.
Rimian
Możesz dodać kolumnę podczas generowania modelu za pomocą wiersza poleceń, na przykład rails g model profile user:references:index address:string bio:text.
duykhoa
1

W zależności od przypadku użycia może być wygodne zawinięcie metody i automatyczne utworzenie asocjacji, gdy nie zostanie znaleziona.

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

teraz wywołanie #profilemetody zwróci skojarzony profil lub utworzy nową instancję.

źródło: Kiedy małpa łata metodę, czy możesz wywołać zastąpioną metodę z nowej implementacji?

Shiyason
źródło
1
w bieżących szyn (testowane na 6.0.2.2) można uprościć do tego: def profile; super || build_profile; end.
glasz
-14

Powinien to być plik has_one. Jeśli buildnie działa, możesz po prostu użyć new:

ModelName.new( :owner => @owner )

jest taki sam jak

@owner.model_names.build
Karl
źródło
11
To nie to samo: jeśli utworzysz nowy model_name z buildem, kiedy @owner zostanie zapisany, zostanie również zapisana nowa nazwa_modelu. Możesz więc użyć kompilacji, aby stworzyć rodzica i dzieci, które zostaną zapisane razem. Tak nie jest, jeśli utworzysz nazwę_modelu z .new
Max Williams