Rails: créer sur l'association has_one

100

Salut (énorme débutant Rails ici), j'ai les modèles suivants:

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

et

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

Lorsque je suis sur le point de créer une nouvelle boutique, j'obtiens l'erreur suivante:

private method `create' called for nil:NilClass

Ceci est mon contrôleur:

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

J'ai essayé différentes variantes en lisant des guides et des tutoriels ici et là, mais je suis plus confus qu'avant et je n'arrive pas à le faire fonctionner. Toute aide serait grandement appréciée.

Neko
la source
Titre de la question modifié pour refléter la question. Duplicate of Using build with a has_one association in rails
Marc-André Lafortune
1
vous pouvez également utiliser@user.build_shop(params)
ImranNaqvi

Réponses:

123

Tout d'abord, voici comment faire ce que vous voulez:

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

Voici maintenant pourquoi votre version n'a pas fonctionné:

Vous avez probablement pensé que cela pourrait fonctionner parce que si l'utilisateur avait une has_manyrelation avec la boutique, @user.shops.create(params[:shop]) cela fonctionnerait. Cependant, il y a une grande différence entre les has_manyrelations et les has_onerelations:

Avec une has_manyrelation, shopsrenvoie un objet de collection ActiveRecord, qui a des méthodes que vous pouvez utiliser pour ajouter et supprimer des boutiques à / d'un utilisateur. L'une de ces méthodes consiste à createcréer une nouvelle boutique et à l'ajouter à l'utilisateur.

Avec une has_onerelation, vous ne récupérez pas un tel objet de collection, mais simplement l'objet Shop qui appartient à l'utilisateur - ou nul si l'utilisateur n'a pas encore de boutique. Puisque ni les objets Shop ni nil n'ont de createméthode, vous ne pouvez pas utiliser createcette méthode avec les has_onerelations.

sepp2k
la source
Merci pour votre réponse, sepp2k. Je vois maintenant pourquoi mon code ne pouvait pas fonctionner.
Neko
118
Vous pouvez également utiliser @user.create_shop(params[:shop]). Voir les méthodes ajoutées par has_one .
nates
La réponse choisie fonctionne, mais la solution @nates fonctionne également. +1 à vous deux.
nfriend21
+1 à la réponse parce que je me posais la même question, +1 à la réponse pour expliquer pourquoi et +1 au commentaire pour donner la meilleure solution.
deivid le
224

Une manière plus concise de le faire est de:

@user.create_shop(params[:shop])

Voir les méthodes ajoutées par has_one dans les guides Ruby on Rails.

nates
la source
6
C'est certainement une meilleure approche
Magnum
7
Attention, si vous create_shop plus d'une fois, cela supprimera la boutique précédente. Par exemple, si vous l'exécutez, @user.create_shop(params[:shop_one_info])cela créera shop_one, MAIS si vous exécutez @user.create_shop(params[:shop_two_info])cela, il supprimera le premier magasin et créera le second.
ecodage5
Le commentaire ci-dessus sur la suppression de la boutique précédente concerne Rails 3.2.18, je ne sais pas pour les versions plus récentes. Impossible de modifier le commentaire après 5 min -_-
ecoding5
J'ai trouvé une solution, je n'ai pas défini l'unicité sur le modèle associé, alors assurez-vous de faire comme comment il est configuré dans le modèle de boutique de cet exemple.
ecodage5
vous pouvez également utiliser@user.build_shop(params)
ImranNaqvi
7

Deux autres façons si vous le souhaitez saveau lieu de create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save
Fellow étranger
la source
1

Juste pour ajouter aux réponses ci-dessus -

@user.create_shop(params[:shop])

La syntaxe ci-dessus crée un nouvel enregistrement, mais elle supprime ensuite l'enregistrement existant similaire.

Sinon, si vous ne souhaitez pas déclencher le rappel de suppression

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

Ce fil pourrait être utile. Cliquez ici

Rais
la source