Rails:Railsでhas_oneアソシエーションでビルドを使用する


143

この例では、userをno profileで作成し、後でprofileそのユーザーのを作成します。has_oneアソシエーションでビルドを使用しようとしましたが、失敗しました。これが機能しているのを見る唯一の方法は、を使用することhas_manyです。userだけ多くても1つで持っていることになっていますprofile

私はこれを試してきました。私が持っています:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

しかし、私がするとき:

user.build_profile 

エラーが発生します:

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

レールで0または1の関連付けを行う方法はありますか?


正確には何を試しましたか?何かコードを投稿していただけませんか?
Ju Nogueira

回答:


359

buildメソッドシグネチャはごとに異なる has_onehas_manyの関連付け。

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

has_many関連付けのビルド構文:

user.messages.build

has_one関連付けのビルド構文:

user.build_profile  # this will work

user.profile.build  # this will throw error

詳細については、has_one関連ドキュメントをご覧ください。


28
has_oneの異なる構文は、常に私を見つけ出します...くそっ!
Galaxy

11
ここで最も高く評価され受け入れられた回答が、OPが尋ねたものとは異なる質問にどのように回答しているかはおかしいです。
Ajedi32 14年

ユーザーがプロファイルに属している場合(ユーザーテーブルのテーブルにforeign_key profile_idがあることを意味します)、ユーザーのプロファイルの作成も上記のように機能しますがuser.build_profile 、編集のみの新しいアクションで、user.build_profile if user.profile.nil? ユーザーの作成中にプロファイルを作成したい場合はaccepts_nested_attributes_for :profile、ユーザーモデル。そして、ユーザーが作成されている形でこれを書い<%= f.simple_fields_for :profile do |p| %>て続行します。
熱意

しかし、なぜこの異なる動作がhas_oneまたはhas_manyで維持されたのですか?設計には何らかの理由があると思います。
好奇心旺盛な2017年

@ Ajedi32回答は質問のタイトルと一致しますが、本文とは一致しません。これが(build_<association>)はRailsで非常に奇妙で予期しない動作であることを考えると、私が何を意味するかを知っている場合、実際の質問の回答よりも多くの人がこの回答を探しています。
Max Williams

19

エラーメッセージをよく見てください。あなたが列を必要としているしていないことを言っているuser_idプロファイルテーブル。モデルに関係を設定することは、答えの一部にすぎません。

また、user_id列をプロファイルテーブルに追加する移行を作成する必要があります。Railsはこれが存在すると想定しており、存在しない場合はプロファイルにアクセスできません。

詳細については、次のリンクをご覧ください。

協会の基本


1
私は自分の問題を理解しました。私が学んでいる本は、外部キーの作成について十分に説明していませんでした。モデルに外部キーを追加する新しい移行を作成しました。ありがとう。
エスピネット

毎回自分で列を作成する必要がありますか?私はそれが自動的に起こったという考えを持っていました。どこでそのアイデアを得たのかわかりません。
リミアン2012年

コマンドラインを使用してモデルを生成するときに、列を追加できますrails g model profile user:references:index address:string bio:text
duykhoa

1

ユースケースによっては、メソッドをラップして、見つからない場合は自動的に関連付けを作成すると便利です。

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

#profileメソッドを呼び出すと、関連付けられたプロファイルが返されるか、新しいインスタンスが作成されます。

ソース: サルがメソッドにパッチを当てているときに、オーバーライドされたメソッドを新しい実装から呼び出すことができますか?


1
現在のレール(6.0.2.2でテスト済み)では、これを次のように簡略化できますdef profile; super || build_profile; end
glasz

-14

である必要がありhas_oneます。buildが機能しない場合は、次のように使用できますnew

ModelName.new( :owner => @owner )

と同じです

@owner.model_names.build

11
これは同じではありません。ビルドで新しいmodel_nameを作成する場合、@ ownerが保存されると、新しいmodel_nameも保存されます。したがって、ビルドを使用して、一緒に保存される親と子を作成できます。これは、.newでmodel_nameを作成する場合には当てはまりません
Max Williams
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.