大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【無職に転生 ~ 就職するまで毎日ブログ出す_28】【Rails】assign_attributes

はじめに

こんにちは、大ちゃんの駆け出し技術ブログです。

タイトルにあるとおり【無職に転生 ~ 就職するまで毎日ブログ出す】というチャレンジをしています!!!!昨日までは就活するまで本気出すでしたが、これだとまるで就活後は頑張らないのかと思われてしまいそうで、、、大人気アニメのタイトルのまるパクリチャレンジです。

https://i.gyazo.com/3a02b7aae4e5e538130d4bb199b55dc7.png

RailsやらRubyやらSQLやらその他Webの知識やらが色々と抜け落ちているのを感じており、知識の定着のためにもアウトプットする機会を増やすためです。加えて、退職して文字通り無職に転生しましてプロニートになり、毎日時間に余裕ができたので引き締めるためにも毎日投稿を思い至りました!

【投稿内容】

  • SQLの難しい処理 (副問合せ、JOINとか複雑な処理が書けない)
  • Rails全般 (純粋に必要な知識が多すぎる、網羅的な理解が足りない)
  • Rubyのあまり使わないメソッドや記述方法 (あまり重要ではないけど特に)
  • Web知識全般 (クッキーやら、セッションやらなんとなくで理解しているものの自分の言葉で説明できない)
  • 書籍 (スタートアップ企業に勤めるので、自分が会社に与える影響やパフォーマンスを高めるためビジネス書を読んでいきます。)

本日やること

assign_attributesという今まで自分が使ったことがないメソッドを使う機会がありましたのでシェアしたいと思います。なお、今回は1つのメソッドのみの記事ですのでだいぶ短くなるかと思いますがご了承ください。

assign_attributes

下記のサイトでは以下のように説明されています。

assign_attributes特定のattributeを変更するためのメソッドです。 attributes=と同じくDBには保存しません。

attributeとはつまり属性です。インスタンス変数ということで間違いありません。引数にはハッシュの値を引数としてレシーバーの属性を変更します。

article.assign_attributes({ title: "Hoge", body: "this is content" })

しかし上記の状態ではDBに保存されていません。なので上記の処理の後にDB保存をする処理をしなければいけません。

article.save!

assign_attributesの使い所

上記の処理を見ると、updateメソッドで1行で書くことができるのがわかると思います。

article.update!({ title: "Hoge", body: "this is content" })

そのため、どこでassign_attributesを使うのだろうと思うかと思います。

使い所としてはupdateアクションで更新処理を一度に行いたくない場合が挙げられると思っています。例えば、前回実装したタグ機能の記事で作成機能を実装しました。

【無職に転生 ~ 就職するまで毎日ブログ出す_25】【Rails】【タグ機能】Materialize のChipsで値をコントローラーに送る方法 - 大ちゃんの駆け出し技術ブログ

class PostController < ApplicationController

  def create
      @post = Post.new(post_params)
      if @post.save_with(tag_names)
        redirect_to @post, notice: t('.success')
      else
        flash.now[:alert] = t('.fail')
        render :new
      end
  end

  private

  def post_params
      params.require(:post).permit(:name, :body)
  end

  def tag_names
    params[:post][:tag_names].split(' ').uniq
  end
end

この記事では実装しませんでしたが、タグ機能なので更新処理も実装する必要がありました。その際に問題だったのが、タグを保存する前に記事を更新しないように制御する必要がありました。例えば、createアクションと同じようにupdateアクションも作ってみます。

def update
  @post.update(post_params)
  if @post.save_with(tag_names)
    redirect_to @post, notice: t('.success')
  else
    flash.now[:alert] = t('.fail')
    render :edit
  end
end

2行目を見るとわかるのですが、updateアクションが実行されるため先に投稿が保存されてしまいます。その後タグを更新する処理が走るようになっていますね。なのでタグの更新に失敗ても、投稿の内容は更新されてしまう処理が走る可能性があります。

ここでupdateアクションをタグ保存時に行う方法もありますが、それだとsave_with(tag_names)メソッドの中身を変える必要があり、作成時と更新時で処理を分ける必要があります。よって少し冗長なコードができてしまいます。

ここで登場するのがassign_attributesメソッドです。既に作成されているインスタンス変数にDBの保存を介さずに値を変更することができるため、下記のようにすればタグ作成機能と同様にタグと同時に投稿が保存される処理が走るようになります。

def update
  @post.assign_attributes(post_params)
  if @post.save_with(tag_names)
    redirect_to @post, notice: t('.success')
  else
    flash.now[:alert] = t('.fail')
    render :edit
  end
end

これによってコードが冗長になることを防ぎます。今のところ自分が思いついた活用シーンがこれぐらいしかなかったのですが、覚えておいて損はないのかなと思いました。

update_all

ついでに記事の中でupdate_allというメソッドを見つけたのでこちらもシェアします。と言ってもかなり簡単です。ですが便利だと思いました。

レシーバーがインスタンス変数の配列で、それらの値を一気にまとめて更新したいときに使われるメソッドです。

Article.where(status: 'deleted').update_all(title: '[deleted]')

SQLで更新処理を行う際は、値の更新範囲は一つではなく複数個もありえます。例えば、以下のようなSQLだと全ての従業員の名前が自分と同名になってしまいます笑

UPDATE employees SET name = 'DAIKI'

そのため、WHERE句を使って更新範囲を狭めますが、それでも複数個の更新はあり得ますね。下記は無理やりですが、、。

UPDATE employees SET name = 'DAIKI' WHERE age > 18

しかし、複数のレコードを意図的に更新したい事はよくあることだと思います。

updateメソッドではレシーバーは必ず一つのオブジェクトでないといけません。その役割を果たすメソッドとして、複数の更新を行うことができるupdate_allメソッドがあるわけです。

終わりに

毎日投稿も28日目!めちゃくちゃ続いたなと思います!ここだけの話ですが、もう実は内定を一社いただいておりまして就職するまでを期限としたこのブログ投稿ももう直ぐ終わりです。ですが、これからもブログは続けていきたいと思っているので是非ともご愛読よろしくお願いします。

ちなみにこの毎日投稿チャレンジが終わり次第本ブログへの投稿は終わりにしようと思います。このはてなブログでの投稿を終わらせ個人ブログサイトを作ってそこに投稿していこうと考えているのでよろしくお願いします。

参考記事

Active Recordのattributesの更新メソッド | 酒と涙とRubyとRailsと