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.


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.


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.


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

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.