Rails: crea sull'associazione has_one


100

Ciao (enorme principiante di Rails qui), ho i seguenti modelli:

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

e

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

Quando sto per creare un nuovo negozio, ricevo il seguente errore:

private method `create' called for nil:NilClass

Questo è il mio controller:

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

Ho provato diverse varianti leggendo guide e tutorial qua e là, ma sono più confuso di prima e non riesco a farlo funzionare. Qualsiasi aiuto sarebbe molto apprezzato.


Titolo della domanda modificato per riflettere la domanda. Duplicato di Using build with a has_one association in rails
Marc-André Lafortune

1
puoi anche usare@user.build_shop(params)
ImranNaqvi

Risposte:


123

Prima di tutto, ecco come fare quello che vuoi:

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

Ora ecco perché la tua versione non ha funzionato:

Probabilmente avete pensato che questo lavoro potrebbe, perché se l'utente ha avuto una has_manyrelazione con regali, @user.shops.create(params[:shop]) avrebbe funzionato. Tuttavia c'è una grande differenza tra has_manyrelazioni e has_onerelazioni:

Con una has_manyrelazione, shopsrestituisce un oggetto di raccolta ActiveRecord, che dispone di metodi che è possibile utilizzare per aggiungere e rimuovere negozi a / da un utente. Uno di questi metodi è create, che crea un nuovo negozio e lo aggiunge all'utente.

Con una has_onerelazione, non si ottiene indietro un tale oggetto da collezione, ma semplicemente l'oggetto Negozio che appartiene all'utente - o nullo se l'utente non ha ancora un negozio. Poiché né gli oggetti Shop né lo zero hanno un createmetodo, non puoi usare createquesto metodo con le has_onerelazioni.


Grazie per la tua risposta, sepp2k. Ora capisco perché il mio codice non può funzionare.
Neko

118
Potresti anche usare @user.create_shop(params[:shop]). Vedi i metodi aggiunti da has_one .
Nates

La risposta scelta funziona, ma funziona anche la soluzione @nates. +1 a entrambi.
nfriend21

+1 alla risposta perché mi chiedevo lo stesso, +1 alla risposta per spiegare perché questo è e +1 al commento per dare la soluzione migliore.
deivid

224

Un modo più conciso per farlo è con:

@user.create_shop(params[:shop])

Vedi i metodi aggiunti da has_one nelle guide Ruby on Rails.


6
Questo è decisamente un approccio migliore
Magnum

7
Fai attenzione che se crei_shop più di una volta verrà eliminato il negozio precedente. Ad esempio, se lo esegui @user.create_shop(params[:shop_one_info]), creerà shop_one, MA se lo esegui @user.create_shop(params[:shop_two_info])eliminerà il primo negozio e creerà il secondo.
ecoding5

Il commento sopra sull'eliminazione del negozio precedente è per Rails 3.2.18, non so delle versioni più recenti. Impossibile modificare il commento dopo 5 min -_-
ecoding5

Trovata una soluzione, non ho impostato l'unicità sul modello associato, quindi assicurati di fare come è impostato nel modello di negozio di questo esempio.
ecoding5

puoi anche usare@user.build_shop(params)
ImranNaqvi

7

Altri due modi se vuoi saveinvece di create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save

1

Solo per aggiungere alle risposte sopra -

@user.create_shop(params[:shop])

La sintassi precedente crea un nuovo record ma successivamente cancella un record esistente simile.

In alternativa, se non si desidera attivare l'eliminazione della richiamata

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

Questo thread potrebbe essere utile. Clicca qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.