Rails: utwórz na asocjacji has_one

100

Cześć (tutaj ogromny nowicjusz Railsów), mam następujące modele:

class Shop < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :title, :user_id, :message => "is already being used"
end

i

class User < ActiveRecord::Base
  has_one :shop, :dependent => :destroy
end

Kiedy mam zamiar utworzyć nowy sklep, pojawia się następujący błąd:

private method `create' called for nil:NilClass

To jest mój kontroler:

@user = current_user
@shop = @user.shop.create(params[:shop])

Próbowałem różnych odmian, czytając przewodniki i samouczki tu i tam, ale jestem bardziej zdezorientowany niż wcześniej i nie mogę go uruchomić. Każda pomoc byłaby bardzo mile widziana.

Neko
źródło
Edytowany tytuł pytania w celu odzwierciedlenia pytania. Duplikat użycia kompilacji z asocjacją has_one w szynach
Marc-André Lafortune
1
możesz również użyć@user.build_shop(params)
ImranNaqvi

Odpowiedzi:

123

Po pierwsze, oto jak robić, co chcesz:

@user = current_user
@shop = Shop.create(params[:shop])
@user.shop = @shop

Oto dlaczego twoja wersja nie działa:

Prawdopodobnie myślał, że ta praca może dlatego, jeśli użytkownik miał has_manystosunek do sklepu, @user.shops.create(params[:shop]) by pracować. Istnieje jednak duża różnica między has_manyrelacjami i has_onerelacjami:

Z has_manyrelacją shopszwraca obiekt kolekcji ActiveRecord, który zawiera metody, których możesz użyć do dodawania i usuwania sklepów do / od użytkownika. Jedną z tych metod jest createutworzenie nowego sklepu i dodanie go do użytkownika.

Z has_onerelacją nie odzyskujesz takiego obiektu kolekcji, ale po prostu obiekt Shop, który należy do użytkownika - lub zero, jeśli użytkownik nie ma jeszcze sklepu. Ponieważ ani obiekty Shop, ani nil nie mają createmetody, nie możesz tego użyć createz has_onerelacjami.

sepp2k
źródło
Dziękuję za odpowiedź, sepp2k. Teraz rozumiem, dlaczego mój kod nie działa.
Neko
118
Możesz również użyć @user.create_shop(params[:shop]). Zobacz metody dodane przez has_one .
nates
Wybrana odpowiedź działa, ale rozwiązanie @nates również działa. +1 dla was obu.
nfriend21
+1 do odpowiedzi, ponieważ zastanawiałem się nad tym samym, +1 do odpowiedzi za wyjaśnienie, dlaczego tak jest i +1 do komentarza za podanie najlepszego rozwiązania.
deivid
224

Bardziej zwięzłym sposobem na to jest:

@user.create_shop(params[:shop])

Zobacz metody dodane przez has_one w przewodnikach po Ruby on Rails.

nates
źródło
6
To zdecydowanie lepsze podejście
Magnum
7
Uważaj, jeśli create_shop więcej niż raz, spowoduje to usunięcie poprzedniego sklepu. Na przykład, jeśli uruchomisz @user.create_shop(params[:shop_one_info]), utworzy shop_one, ALE jeśli uruchomisz @user.create_shop(params[:shop_two_info]), usunie pierwszy sklep i utworzy drugi.
ekodowanie5
Powyższy komentarz dotyczący usunięcia poprzedniego sklepu dotyczy Railsów 3.2.18, nie wiem o nowszych wersjach. Nie można edytować komentarza po 5 minutach -_-
ecoding5
Znalazłem rozwiązanie, nie ustawiłem niepowtarzalności w powiązanym modelu, więc upewnij się, że robisz to, jak to jest skonfigurowane w modelu sklepu tego przykładu.
ekodowanie5
możesz też użyć@user.build_shop(params)
ImranNaqvi
7

Jeszcze dwa sposoby, jeśli chcesz savezamiast create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save
Kolega nieznajomy
źródło
1

Wystarczy dodać do powyższych odpowiedzi -

@user.create_shop(params[:shop])

Powyższa składnia tworzy nowy rekord, ale następnie usuwa podobny istniejący rekord.

Alternatywnie, jeśli nie chcesz wywoływać funkcji usuwania wywołania zwrotnego

Shop.create(user_id: user.id, title: 'Some unique title')

Ten wątek może być pomocny. Kliknij tutaj

Rais
źródło