Rails: Utilisation de build avec une association has_one dans les rails


143

Dans cet exemple, je crée un useravec non profile, puis je crée plus tard un profilepour cet utilisateur. J'ai essayé d'utiliser build avec une has_oneassociation mais ça a explosé. La seule façon dont je vois ce fonctionnement est d'utiliser has_many. Le userest censé n'en avoir au plus qu'un profile.

J'ai essayé ceci. J'ai:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Mais quand je fais:

user.build_profile 

J'obtiens l'erreur:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Y a-t-il un moyen dans les rails d'avoir 0 ou 1 association?


qu'avez-vous essayé exactement? pourriez-vous, s'il vous plaît, poster un code?
— Ju Nogueira

Réponses:


359

La buildsignature de la méthode est différente pour les associations has_oneet has_many.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

La syntaxe de construction pour l' has_manyassociation:

user.messages.build

La syntaxe de construction pour l' has_oneassociation:

user.build_profile  # this will work

user.profile.build  # this will throw error

Lisez la documentation de l' has_oneassociation pour plus de détails.


28
La syntaxe différente du has_one me surprend toujours ... bon sang!
— Galaxie

11
C'est drôle de voir comment la réponse la mieux notée et acceptée ici répond à une question différente de celle posée par l'OP.
— Ajedi32

Soi-disant si l'utilisateur appartenait au profil (ce qui signifie que la table utilisateur a une clé étrangère profile_id dans sa table), la création d'un profil pour l'utilisateur fonctionnera comme mentionné ci-dessus, c'est-à-dire, mais pour une nouvelle action uniquement user.build_profile pour modifier user.build_profile if user.profile.nil? et si vous souhaitez créer un profil lors de la création de l'utilisateur, écrivez accepts_nested_attributes_for :profile-le dans Modèle utilisateur. et sous la forme de quel utilisateur est créé, écrivez <%= f.simple_fields_for :profile do |p| %>ceci et continuez.
— zèle

mais pourquoi ce comportement différent a-t-il été conservé pour has_one ou has_many? Il y aurait une raison lors de la conception, je pense et j'attends.
— curieux

@ Ajedi32 la réponse correspond au titre de la question mais pas au corps. Étant donné que ceci ( build_<association>) est un comportement assez étrange et inattendu dans Rails, il y a beaucoup plus de gens à la recherche de cette réponse que la réponse aux questions réelles, si vous voyez ce que je veux dire.
— Max Williams

19

Jetez un œil au message d'erreur. Cela vous indique que vous n'avez pas de colonne obligatoire user_iddans la table de profil . La définition des relations dans le modèle n'est qu'une partie de la réponse.

Vous devez également créer une migration qui ajoute la user_idcolonne à la table de profil. Rails s'attend à ce que ce soit là et si ce n'est pas le cas, vous ne pouvez pas accéder au profil.

Pour plus d'informations, veuillez consulter ce lien:

Bases de l'association


1
Je viens de comprendre mon problème. Le livre dont j'apprends n'a pas très bien expliqué la création de clé étrangère. J'ai créé une nouvelle migration qui ajoute une clé étrangère à mon modèle. Merci.
— espinet

Avez-vous besoin de créer la colonne vous-même à chaque fois? J'ai eu cette idée que cela se produisait automatiquement. Je ne sais pas d'où j'ai eu cette idée.
— Rimian

Vous pouvez ajouter la colonne lors de la génération d'un modèle en utilisant la ligne de commande, quelque chose comme rails g model profile user:references:index address:string bio:text.
— duykhoa le

1

Selon le cas d'utilisation, il peut être pratique d'encapsuler la méthode et de créer automatiquement l'association lorsqu'elle n'est pas trouvée.

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

maintenant appeler la #profileméthode retournera le profil associé ou créera une nouvelle instance.

source: Lorsque le singe corrige une méthode, pouvez-vous appeler la méthode remplacée à partir de la nouvelle implémentation?


1
dans des rails de courant (testé sur 6.0.2.2) , vous pouvez simplifier à: def profile; super || build_profile; end.
— glasz le

-14

Cela devrait être un has_one. Si cela buildne fonctionne pas, vous pouvez simplement utiliser new:

ModelName.new( :owner => @owner )

est le même que

@owner.model_names.build

11
Ce n'est pas la même chose: si vous créez un nouveau nom_modèle avec build, lorsque @owner est enregistré, le nouveau nom_modèle sera également enregistré. Ainsi, vous pouvez utiliser build pour créer un parent et des enfants qui seront enregistrés ensemble. Ce n'est pas le cas si vous créez un nom_modèle avec .new
— Max Williams
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.