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?


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.


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


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?


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.