154 votes

Rails : Utilisation de build avec une association has_one dans Rails

Dans cet exemple, je crée un utilisateur sans profil, puis je crée ultérieurement un profil pour cet utilisateur. J'ai essayé d'utiliser build avec une association has_one mais cela a échoué. La seule façon que je vois de le faire fonctionner est d'utiliser has_many. L' utilisateur est censé avoir au plus un profil.

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 

Je reçois 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 en rails d'avoir une association à 0 ou 1?

0 votes

Qu'as-tu exactement essayé? Pourrais-tu, s'il te plaît, poster du code?

386voto

Harish Shetty Points 38877

La signature de la méthode build est différente pour les associations has_one et has_many.

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

La syntaxe build pour l'association has_many:

user.messages.build

La syntaxe build pour l'association has_one:

user.build_profile  # fonctionne

user.profile.build  # génère une erreur

Lisez la documentation sur l'association has_one pour plus de détails.

31 votes

Les différentes syntaxes pour le has_one me prennent toujours au dépourvu... zut !

12 votes

C'est drôle 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.

0 votes

Supposément si l'utilisateur appartenait à un profil (ce qui signifie que la table de l'utilisateur a une clé étrangère profile_id dans sa table), alors la création d'un profil pour l'utilisateur fonctionnera comme mentionné ci-dessus, mais pour une nouvelle action uniquement user.build_profile pour éditer user.build_profile si user.profile.nil? et si vous souhaitez créer un profil en même temps que l'utilisateur, alors écrivez accepts_nested_attributes_for :profile ceci dans le modèle User. et dans le formulaire où l'utilisateur est créé, écrivez <%= f.simple_fields_for :profile do |p| %> cela et continuez.

19voto

sosborn Points 11273

Regardez attentivement le message d'erreur. Il vous dit que vous n'avez pas la colonne requise user_id dans la table de profil. Définir les relations dans le modèle n'est qu'une partie de la réponse.

Vous devez également créer une migration qui ajoute la colonne user_id à la table de profil. Rails s'attend à ce qu'elle soit présente et si ce n'est pas le cas, vous ne pourrez pas accéder au profil.

Pour plus d'informations, veuillez consulter ce lien :

Notions de base sur les associations

1 votes

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

0 votes

Avez-vous besoin de créer la colonne vous-même à chaque fois? J'avais cette idée que cela se faisait automatiquement. Je ne sais pas d'où me vient cette idée.

0 votes

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.

1voto

Shiyason Points 457

En fonction du cas d'utilisation, il peut être pratique d'encapsuler la méthode et de construire automatiquement l'association lorsque celle-ci n'est pas trouvée.

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

maintenant, l'appel de la méthode #profile renverra soit le profil associé, soit construira une nouvelle instance.

source: Lors du monkey patching d'une méthode, pouvez-vous appeler la méthode remplacée depuis la nouvelle implémentation ?

1 votes

Dans Rails actuel (testé sur 6.0.2.2), vous pouvez simplifier ceci en : def profile; super || build_profile; end.

-14voto

Karl Points 2712

Il devrait être un has_one. Si build ne fonctionne pas, vous pouvez simplement utiliser new:

ModelName.new( :owner => @owner )

est la même que

@owner.model_names.build

11 votes

Ce n'est pas la même chose : si vous créez un nouveau model_name avec build, lorsque @owner est enregistré, le nouveau model_name 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 model_name avec .new

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X