web-dev-qa-db-ja.com

Railsで、レコードを更新したり、存在しない場合に新しいレコードを作成したりするための最良の方法は何ですか?

一部のモデルにはcreateステートメントがありますが、レコードがすでに存在するかどうかに関係なく、結合テーブル内にレコードが作成されます。

私のコードは次のようになります:

@user = User.find(current_user)
@event = Event.find(params[:id])
for interest in @event.interests
 @user.choices.create(:interest => interest, :score => 4)
end

問題は、何があってもレコードを作成することです。レコードが存在しない場合にのみレコードを作成します。レコードが存在する場合は、見つかったレコードの属性を使用して、1を加算または減算します。

私は周りを見回してきましたが、find_or_create_by。レコードが見つかるとどうなりますか?現在の:score属性と1を追加します。

idで検索または作成できますか?探しているモデルはid外部キーとスコア属性のみを持つ結合モデルであるため、どの属性で検索できるかわかりません。

私は試した

@user.choices.find_or_create_by_user(:user => @user.id, :interest => interest, :score => 4)

しかし得た

未定義のメソッドfind_by_user

私は何をすべきか?

26
ChrisWesAllen

Choiceモデルにuser_id(ユーザーに関連付ける)とinterest_id(インタレストに関連付ける)があるとすると、次のようなことがうまくいくはずです。

@user = User.find(current_user)
@event = Event.find(params[:id])

@event.interests.each do |interest|
  choice = @user.choices.find_or_initialize_by_interest_id(interest.id) do |c|
    c.score = 0 # Or whatever you want the initial value to be - 1
  end

  choice.score += 1
  choice.save!
end

いくつかのメモ:

  1. user_idに属するchoicesのみをフェッチするようにすでにRailsに指示しているため、find_or_*_by_*@user列を含める必要はありません。
  2. 私は基本的にfind_or_initialize_by_*と同じfind_or_create_by_*を使用していますが、initializeは実際にはレコードを作成しないという大きな違いがあります。これは、Model.newではなく、Model.createに似ています。
  3. c.score = 0を設定するブロックは、レコードにnotが存在する場合にのみ実行されます。
  4. choice.score += 1は、レコードが存在するかどうかに関係なく、レコードのスコア値を更新します。したがって、デフォルトのスコアc.score = 0は、初期値から1を引いた値になります。
  5. 最後に、choice.save!はレコードを更新する(既に存在する場合)か、開始されたレコードを作成します(存在しない場合)。
23
vonconrad
my_class = ClassName.find_or_initialize_by_name(name)

my_class.update_attributes({
   :street_address => self.street_address,
   :city_name => self.city_name,
   :Zip_code => self.Zip_code
})
60
krunal shah

find_or_create_by_user_idよく聞こえる

7
apneadiving

また、Railsでは次のことができます:

@user.choices.where(:user => @user.id, :interest => interest, :score => 4).first_or_create
3
Zuhaib Ali

Rails 4を使用している場合は、これまでのようなFinderメソッドが作成されるとは思わないため、find_or_create_by_userは作成されません。代わりに、次のようにします。

@user = User.find(current_user)
@event = Event.find(params[:id])
for interest in @event.interests
 @user.choices.find_or_create_by(:interest => interest) do |c|
  c.score ||= 0
  c.score += 1
 end
end
0
rainkinz

In Rails 4find_or_create_byを使用してオブジェクトを取得し(存在しない場合は作成されます)、次にupdateを使用してレコードを保存または更新します。更新メソッドは、レコードが存在しない場合はそれを保持し、それ以外の場合はレコードを更新します。

例えば

@edu = current_user.member_edu_basics.find_or_create_by(params.require(:member).permit(:school))

if @edu.update(params.require(:member).permit(:school, :majoy, :started, :ended))
0
Albert.Qing