Home Software Libraries Ruby rails Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業 @wakairo 2026/03/10 22:36 Rails 8.1におけるRails.configuration.action_view.remove_hidden_field_autocomplete config.action_view.remove_hidden_field_autocompleteは、hiddenフィールドからautocomplete="off"属性を除去するかどうかの設定です。 (この機能のPRとこの機能のCommit) bin/rails app:updateコマンドが生成する config/initializers/new_framework_defaults_8_1.rbには、 以下のようにこの設定を有効にするコードがあります。 + # Highlight code that triggered redirect in logs. + config.action_dispatch.verbose_redirect_logs = true + この機能が導入された背景 この機能は、HTML標準へ より準拠したHTMLをRailsが生成するように追加されました。 具体的には、この機能を有効にしない場合、Railsは以下のようなautocomplete="off"属性を付けたhiddenフィールドを生成していました。 <input type="hidden" name="authenticity_token" value="abc123..." autocomplete="off"> このautocomplete属性付きのhiddenフィールドはHTML標準に沿っておらず、 Nu Html Checkerは 以下のメッセージとともに「エラー」として指摘していました。 An “input” element with a “type” attribute whose value is “hidden” must not have an “autocomplete” attribute whose value is “on” or “off”.
Home Software Libraries Ruby rails Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業 @wakairo 2026/03/09 21:22 Rails 8.1におけるconfig.action_dispatch.verbose_redirect_logs config.action_dispatch.verbose_redirect_logsは、リダイレクトのソース位置を関連するログ行の下にログ出力するかどうかの設定です。 rails newで生成されるconfig/environments/development.rbにおいて、 8.1からは以下のようにこの設定を有効にするコードが追加されました(この変更のPRとこの変更のCommit)。 + # Highlight code that triggered redirect in logs. + config.action_dispatch.verbose_redirect_logs = true + development環境でデバッグする際に使える情報が増える形になると思いますので、既存アプリでもこの設定を追加しても良いかもしれません。
Home Software Libraries Ruby rails Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業 @wakairo 2026/03/09 21:08 基本的にはRailsガイドの手順に従えば良いと思います。 ガイドの手順にもありますが、ぜひbin/rails app:updateコマンドを活用しましょう。 Rails 8.1への移行で対応が必要そうな個別の作業について、以下のコメントでそれぞれ取り上げますので、ご参考になれば幸いです。
Home Software Libraries Ruby activerecord Active Recordのgenerates_token_for:DBカラム不要で一時的なトークンを扱う機能 @wakairo 2026/03/02 17:02 generates_token_forとは generates_token_forは、特定の目的を持つトークンを生成し、そのトークンからレコードを検索・検証するための機能です。 Rails 7.1で標準機能として導入されました。 特長 DBへのデータ保存不要:トークンを保存するためのデータベースのカラムやテーブルを追加せずに利用できます。 目的別に分離:目的(purpose)ごとにトークンを分離できるため、別目的で生成されたトークンを使い回すことができません。 失効条件を設定可能:有効期限とモデルのデータ変化による失効条件を柔軟に設定できます。 改ざん耐性:署名付きのトークンのため、改ざん耐性があります。 使いどころ:一時的に有効なURLの送付 以下のような「一時的で目的が限定されたトークン付きURL」を送付する場面に適しています。 メールアドレス確認リンク 招待リンク マジックリンク(パスワードなしログイン) 一時アクセスリンク なお、APIの認証トークンのような永続的な用途には向きません。 Railsで永続的なトークンを扱いたい場合は、has_secure_tokenなどの利用を検討してください。 注意:パスワードリセットには専用APIあり Rails 8.0以降でhas_secure_passwordを利用している場合、パスワードリセット用トークンを扱う専用APIが自動的に提供されます。そのため、パスワードリセットの用途に限っては、自分でgenerates_token_forを定義する必要はありません。(詳しくは後述の補足を参照してください) 使い方(メールアドレス確認の例) ここでは「メールアドレス変更時の確認リンク」を題材に、基本的な使い方を説明します。 基本の流れ(定義・生成・検証) まず、モデルにgenerates_token_forを用いて、トークンの用途名(ここでは:email_verification)を定義します。 なお、用途名が異なっていれば複数定義することも可能です。 class User < ApplicationRecord generates_token_for :email_verification end トークンを生成する際は、対象のインスタンスに対してgenerate_token_forを呼び出します。 注意:モデルで定義する際は複数形のgenerates_token_forですが、 インスタンスから呼び出す際は単数形のgenerate_token_forになる点に注意してください。 user = User.first # 対象のレコードを取得(ここでは`first`で代替) token = user.generate_token_for(:email_verification) # => "BAhJIi..." (生成されたトークン文字列) 生成したトークンは、例えば以下のようにURLパラメータに載せてメールなどで送付するのが典型的な流れです。 # 例:メイラー等でのURL生成 email_verifications_url(token: token) # => "https://example.com/email_verifications?token=BAhJIi..." 受け取ったトークンからレコードを検索・検証するには、クラスメソッドのfind_by_token_forを使います。 有効なトークンならUserインスタンスが返り、無効なトークンや期限切れのトークンの場合はnilが返ります。 user = User.find_by_token_for(:email_verification, token) 有効期限による失効 定義時にexpires_inオプションを付与することで、トークンに有効期限(例:15分間)を設けることができます。 class User < ApplicationRecord generates_token_for :email_verification, expires_in: 15.minutes end モデルのデータ変化による失効 generates_token_forは、「一度状態が変わったら、古いトークンを使わせない」挙動を簡単に実装できます。 例えば、新しいメールアドレス確認用に生成されたトークンを、メールアドレスの変更が完了した後に無効化すれば、万が一流出しても悪用を防げます。 無効化の仕組みは「定義時のブロックの戻り値をトークンに埋め込み、検証時にその値が変わっていれば無効とみなす」というものです。 class User < ApplicationRecord generates_token_for :email_verification, expires_in: 15.minutes do # トークン生成時と検証時で`email`の値が異なれば(=メールアドレスが変更完了していれば)、 # 有効期限内であっても無効になる email end end 注意:ブロックの戻り値に機密情報を含めない ブロック内で返した値は、改ざん耐性はあるものの、暗号化されるわけではなく、デコードすれば読める形でトークンに埋め込まれます。 そのため、ブロックの戻り値には機密情報を含めてはなりません。 補足:パスワードリセット専用API Rails 8.0以降では、モデルでhas_secure_passwordを利用している場合、パスワードリセットトークン用の以下のメソッドがデフォルトで追加されます。 password_reset_token:トークン生成(インスタンスメソッド) find_by_password_reset_token:トークンからの検索と検証(クラスメソッド) find_by_password_reset_token!:同上(無効時に例外を発生させるバージョン) Rails 8.1で追加されたトークン有効期限の機能 さらにRails 8.1以降では、トークンの有効期限を柔軟に扱うための機能が追加されています。 has_secure_passwordの呼び出し時にreset_tokenオプションを通して有効期限を変更できるようになりました。 class User < ApplicationRecord has_secure_password reset_token: { expires_in: 1.hour } end また、以下のメソッドが追加されました。 password_reset_token_expires_in:設定されている有効期限の取得(インスタンスメソッド) 参考情報 Rails APIドキュメント:generates_token_for Rails APIドキュメント:has_secure_password GitHub:has_secure_passwordの実装コード
Home Software Libraries Ruby rails Railsモデルのクラスメソッドはメソッドチェーンでも呼べる ― 注意点とscopeへ整理する方針 @wakairo 2026/02/23 18:55 Railsでモデルにクラスメソッドを定義すると、メソッドの内容に関わらず、メソッドチェーン(ActiveRecord::Relation)経由でもそのメソッドを呼び出せます。 これが便利な場合がある一方で、エラーにはならないものの、意味的に不正確なコードが書けてしまったり、気を付けないとバグの温床になったりすることもあります。 特に、リレーションを返す用途のクラスメソッドと、そうでないクラスメソッドが混在している場合に混乱が生じやすくなります。 本記事ではその仕組みと注意点、そして実務上の整理方針を解説します。 モデルのクラスメソッドがActiveRecord::Relation経由でも呼べる仕組み 以下のようにPenクラスにクラスメソッドを定義したとします。 class Pen < ApplicationRecord def self.with_color(color) if color.present? where(color: color) else all end end end 定義したこのメソッドは以下のように呼べます。これはRubyにおける通常のクラスメソッド呼び出しです。 test(dev):000> Pen.with_color("red") Pen Load (0.1ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007b3619af2060 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007b361a437f80 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] では、メソッドチェーンでの呼び出しはどうなっているでしょうか。 まず、whereなどのスコープメソッドを呼び出した結果は、以下で確認できる通り、 PenクラスではなくPen::ActiveRecord_Relationのインスタンスになります。 test(dev):000> Pen.where(created_at: 1.year.ago..).class => Pen::ActiveRecord_Relation このリレーションインスタンスに対しても、クラスメソッドを以下のように呼び出せます。 つまり、モデルに定義したクラスメソッドは、そのモデルに対応したActiveRecord::Relationクラスのインスタンスからも呼べる仕組みになっているのです。この仕組みが、クラスメソッドをメソッドチェーンから呼べるようにしています。 test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("red").count Pen Count (0.5ms) SELECT COUNT(*) FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 07:00:00.454690' AND "pens"."color" = 'red' => 2 メソッドチェーンで呼べてしまうからこそ、モデルのクラスメソッドは責務と名前を明確にする with_colorのようにクエリを組み立てるメソッドであれば、メソッドチェーンで呼び出せても違和感がないと思います。 しかし、データベースクエリと関係のないメソッドでもクラスメソッドであれば同様にリレーション経由で呼び出せてしまいます。 その結果、クラスメソッドであれば、その処理内容に関わらず、クエリを組み立てるメソッドを間に挟んだメソッドチェーンにおいて、エラーにならず実行できてしまいます。 具体例を見てみましょう。export_to_csvがクラスメソッドだとすると以下のようにwhereを間に挟んでもエラーにならずに実行できます。 Pen.where(created_at: 1.year.ago..).export_to_csv(1, 2) ではexport_to_csvメソッドは、countのように「条件に合ったレコードに対する処理をするメソッド」なのでしょうか? それとも、クエリ構築とは関係がない「ユーティリティメソッド」で、このメソッドチェーンで呼び出すコードは何かしらのミスの結果なのでしょうか? この例から分かるのは、モデルクラスに定義するクラスメソッドの名前はより慎重に付けなくてはならないということです。 名前だけではありません。メソッドの責務についても、クエリ用途であるか否かなど、明確さがあることが望まれます。 そのため、モデルのクラスメソッドの責務と名前に一定の規則性を持たせることも1つのアイデアです。 例えば、「findやcreateなど、Active Recordのクエリメソッドで使われる単語から名前が始まる場合は、必ずクエリ関連の責務とする」といったルールが考えられます。 また、名前だけでなくコードドキュメントにおいても、他のメソッド以上に使い方をはっきりと示すことが望まれます。 クエリ構築用クラスメソッドでelse節を省くとメソッドチェーンの途中でエラーを招く クエリ構築用のクラスメソッドの場合には、条件分岐のelse節を省いてしまうと、メソッドチェーンの途中で予期せぬエラーを引き起こす原因になるという注意点もあります。 以下のように、先ほどのwith_colorメソッドからelse節を削除してみましょう。 class Pen < ApplicationRecord def self.with_color(color) if color.present? where(color: color) end end end 引数が有効値のとき(color.present?が真になるとき)はエラーになりませんが、 以下のように引数が空文字列で条件を満たさない場合はNoMethodErrorが発生してしまいます。 test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("").count (test):0:in '<main>': undefined method 'count' for nil (NoMethodError) これは、以下のように条件を満たさなかった場合の戻り値がnilになってしまうためです。 nilに対してcountメソッドを呼び出そうとしたため、エラーになったのです。 test(dev):000> Pen.where(created_at: 1.year.ago..).with_color("") => nil クエリ構築用メソッドとしてクラスメソッドを設計するなら、 常にActiveRecord::Relationインスタンス(またはそれに準ずるオブジェクト)を返す設計にしておくことが求められます。 リレーションを返す場合はscopeに統一する クエリ構築の一環としてリレーションを返すことを意図したメソッドは、 クラスメソッドではなくscopeでの定義に統一するのが有効です。 これは、前述したモデルのクラスメソッドに関する困難さを和らげる、シンプルなアプローチの1つです。 1つのプロジェクト内でクラスメソッドでの定義とscopeでの定義を混在させることは、 前述のクラスメソッドの注意点とscopeの注意点を足し合わせることになり、チーム開発における理解コストを上げてしまいます。 では、混在をやめてどちらに統一するのが良いかと言えば、scopeに統一することが有力な戦略です。以下にその理由を3つ述べます。 理由1: scopeとクラスメソッドでは挙動に違いがある まず大きな違いは、nilまたはfalseを返したときの挙動です(この点については理由3で詳しく後述します)。 また、レシーバとしてモデル名を書いた際にも明確な挙動の違いが確認できます(下記「参考」を参照)。 これらに限らず、他の挙動の差異が潜んでいる可能性もあります。 このように仕様や挙動に差異がある場合、二通りのやり方が混在していると「片方で問題ない書き方がもう片方でバグになる」という厄介さを抱えることになります。また、両方の仕様・挙動に対する知識が求められるため、コードベースの理解コストも上がってしまいます。 (参考)モデルクラス名を明示的にレシーバとした場合の挙動の違い クエリ構築を行うscopeとクラスメソッドでは、モデルクラス名を明示的にレシーバとして書いてしまうと、 前の条件を引き継ぐか否かに違いが出てしまいます。 具体的には、まず以下のように、Pen.where(color: color)とだけ記述したscopeとクラスメソッドをそれぞれ定義します。 class Pen < ApplicationRecord scope :with_color_s, ->(color) { Pen.where(color: color) } def self.with_color_m(color) Pen.where(color: color) end end 次に、それぞれを実行してみると、以下のような違いが出ます。 test(dev):000> Pen.where(price: 80).with_color_m("red") Pen Load (0.2ms) SELECT "pens".* FROM "pens" WHERE "pens"."price" = 80 AND "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007ad76b175648 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">] test(dev):000> Pen.where(price: 80).with_color_s("red") Pen Load (0.2ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007ad76ae02d08 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007ad76ae02bc8 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] クラスメソッドのwith_color_mは前の条件where(price: 80)が適切にマージされているのに対し、 scopeのwith_color_sはprice: 80の条件が無視されています(where(color: color)のようにレシーバPenを消せば正しくマージされます)。 なお、この違いの確認には執筆時の最新版であるRails 8.1.2を用いました。 理由2: scopeには「リレーションを返す」という共通認識がある クラスメソッドは、用途が極めて広い汎用的な仕組みです。そのため、メソッド名だけでは「チェーン可能なクエリメソッドなのか」が判断しづらくなります。 一方、scopeは「クエリを組み立ててActiveRecord::Relationを返すためのもの」という開発者間の共通認識があります。 また、scopeは通常のクラスメソッド定義方法とは異なるため、メソッドチェーンで利用できることも自然に受け入れられます。 以上から、scopeの方が、コードの読み書きに必要な認知的負荷が低いと言えます。 理由3: scopeでは、else節を省いた簡潔な記載が可能 RailsのAPIドキュメントによれば、 nilまたはfalseが返された場合にその時点までに構築済みのクエリ条件(allに相当)を返すというのがscopeの仕様です。 そのため、以下のscope定義にはelse節がありませんが、メソッドチェーンの途中で呼び出されてもエラーを引き起こしません。 class Pen < ApplicationRecord scope :with_color, ->(color) do if color.present? where(color: color) end end end これにより、先ほど述べたクラスメソッドが持つ「nilを返してチェーンが壊れる」問題を回避しつつ、条件付きクエリを簡潔に表現できます。 さらにRubyでは、全く同じ処理を以下の一行に書き直せます。 class Pen < ApplicationRecord scope :with_color, ->(color) { where(color: color) if color.present? } end このようにscopeを使うことで、意図が明確かつ簡潔なコードを書くことが出来ます。 まとめ クエリ用途ではないクラスメソッドを定義するときには、メソッドチェーンで呼び出されることが不自然であると伝わるように、責務や名前の明確さが重要です。 リレーションを返すメソッドをscopeでの定義に統一することで、nil返却エラーを防いだり、コードの読み書きを容易にしたり出来ます。
Home Software Libraries Ruby activerecord Active Recordのallが持つ条件を引き継ぐ性質と動的クエリ構築への応用 @wakairo 2026/02/22 15:32 RailsのActive Recordのallメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。 本記事では、このallの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。 allは「構築済みの条件」を引き継ぐ データベースにPenモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。 test(dev):000> Pen.all Pen Load (1.2ms) SELECT "pens".* FROM "pens" /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf641ba6f0 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b23208 id: 2, color: "blue", price: 80, created_at: "2026-02-20 05:53:03.457025000 +0000", updated_at: "2026-02-20 05:53:03.457025000 +0000">, #<Pen:0x00007cdf66b230c8 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] では、別の条件に続けてallを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。 test(dev):000> Pen.where(color: "red").all Pen Load (0.1ms) SELECT "pens".* FROM "pens" WHERE "pens"."color" = 'red' /* loading for pp */ LIMIT 11 => [#<Pen:0x00007cdf66b20648 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">, #<Pen:0x00007cdf66b20508 id: 3, color: "red", price: 150, created_at: "2026-02-20 05:53:10.752331000 +0000", updated_at: "2026-02-20 05:53:10.752331000 +0000">] このように、allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。 応用例:動的クエリ構築においてallを起点にしてメソッドチェーン可能にする この「そこまでに構築された条件を引き継ぐ」というallの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。 例えば、クエリパラメータを処理するfilter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。 test(dev):000> Pen.where(created_at: 1.year.ago..).filter_by(color: "red", max_price: 100) Pen Load (0.4ms) SELECT "pens".* FROM "pens" WHERE "pens"."created_at" >= '2025-02-21 05:50:22.982591' AND "pens"."color" = 'red' AND "pens"."price" <= 100 /* loading for pp */ LIMIT 11 => [#<Pen:0x000072d2c03393d8 id: 1, color: "red", price: 80, created_at: "2026-02-20 05:52:53.460900000 +0000", updated_at: "2026-02-20 05:52:53.460900000 +0000">] この挙動を実現するfilter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。 class Pen < ApplicationRecord scope :filter_by, ->(params) do pens = all pens = pens.where(color: params[:color]) if params[:color].present? pens = pens.where(price: ..params[:max_price]) if params[:max_price].present? pens end end ここで重要になるのが、filter_byの中で最初にallを呼んで変数に入れている点です。 allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。 (参考)個別スコープへの切り出しとそのタイミング 先ほどの例ではfilter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorやpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。 Active Recordのスコープは、ブロックの評価結果がnilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。 class Pen < ApplicationRecord scope :with_color, ->(color) { where(color: color) if color.present? } scope :with_max_price, ->(max_price) { where(price: ..max_price) if max_price.present? } scope :filter_by, ->(params) do with_color(params[:color]).with_max_price(params[:max_price]) end end なお、YAGNI原則の観点からは、 これらの個別スコープ(with_colorやwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。
Home Software Libraries Ruby rails RailsのAction MailerとAction Mailboxはmail gemを利用している @wakairo 2026/02/07 12:07 RailsでMailクラスに遭遇したら、それはmail gemのものです。ですので、そのAPI情報などは、Railsの情報源ではなく、以下のようなmail gemの情報源を見ると得られます。 GitHub: https://github.com/mikel/mail APIドキュメント: https://www.rubydoc.info/gems/mail/
Home Software Libraries Ruby activerecord Active Recordマイグレーション:t.referencesとadd_referenceで外部キー制約を詳細設定する方法 @wakairo 2026/02/05 13:18 要点 Active Recordのマイグレーションファイルにてt.referencesまたはadd_referenceを使う際、 foreign_key:オプションに対してtrueの代わりに ハッシュを渡すことで外部キー制約の詳細を設定 できます。 このハッシュで指定できるオプションはadd_foreign_keyのAPIドキュメントに記載されているオプションです。 具体例t.references を使う場合(新規テーブル作成時)create_table :books do |t| t.references :author, foreign_key: { on_delete: :cascade } end add_reference を使う場合(既存テーブルへの追加)add_reference :books, :author, foreign_key: { on_delete: :cascade } 説明 Active Recordのマイグレーションファイルにてテーブル間に関連付けをする際には、 t.referencesまたはadd_referenceを使うと便利です。 この2つのメソッドは、呼び出すときにforeign_key:オプションを付けると、 外部キー制約も同時に付与することが出来ます。例えば、foreign_key: trueという記述をよく見かけます。 foreign_key: trueは外部キー制約をかけることだけの指定となりますが、 ここでのtrueの代わりにハッシュを渡すことで、外部キー制約の詳細が設定可能になります。 このハッシュの中に入れられるオプション、つまり外部キー制約の詳細設定項目は、add_foreign_keyに渡せるオプションと同じです。 このadd_foreign_keyに渡せるオプションの具体的な種類とそれぞれの意味については、 add_foreign_keyのAPIドキュメントをご覧ください。
Home Software Libraries Ruby activerecord Active Recordマイグレーションでのadd_foreign_keyとadd_referenceの違い @wakairo 2026/01/30 21:14 Railsガイドを一読しただけでは、 add_foreign_keyとadd_referenceが、それぞれどのようなもので、どう違うのかがいまいちよく分かりませんでした。 そこで、add_foreign_keyとadd_referenceによってdb/schema.rbがどのように変化するかを基に、 それぞれの機能について確認してみました。 add_foreign_key add_foreign_keyは外部キー制約の追加だけを行います。よって、カラムやインデックスの追加は行いません。 外部キー制約とは、子テーブルが参照している親のIDが親テーブルに存在することをデータベースレベルで保証する制約です。 例えば、以下の記述をマイグレーションファイルに行ったとします。 add_foreign_key :products, :users すると以下の記述がdb/schema.rbに追加されます。 add_foreign_key "products", "users" この記述は、products(子テーブル)のuser_idカラムに登場するIDが、users(親テーブル)のidカラムに必ず存在するように制約をかけています。 add_reference add_referenceは、テーブル間の関連付けに関する複数の設定を一度に行える便利メソッドです。 add_referenceの基本機能は、カラムとインデックスの追加です。 例えば、以下の記述をマイグレーションファイルに行ったとします。 add_reference :products, :user すると以下のように、users(親テーブル)に関連付ける2つの記述、具体的にはuser_idのカラムとインデックスをproducts(子テーブル)に追加する2つの記述が、db/schema.rbに追加されます。 t.integer "user_id" t.index ["user_id"], name: "index_products_on_user_id" またadd_referenceは、foreign_key:trueオプションを追加することで、前述の2つに加えて外部キー制約も同時に設定できます。 例えば、以下の記述をマイグレーションファイルに行ったとします。 add_reference :products, :user, foreign_key:true すると以下のように、前述の2つの記述に加えて、add_foreign_keyの記述がdb/schema.rbに追加されます。 t.integer "user_id" t.index ["user_id"], name: "index_products_on_user_id" add_foreign_key "products", "users" (参考)add_referenceとt.referencesの機能は基本的に同じ add_referenceとcreate_tableのブロックの中で呼び出すt.referencesは基本的に同じ機能を提供します(オプション以外の引数の部分で違いはありますが。) 実際にt.referencesへ渡せるオプションはadd_referenceと同じです。 また、t.referencesの実装とadd_referenceの実装のどちらもReferenceDefinition.newを内部で呼ぶ形になっています(v8.1.2で確認)。
Home Software Libraries Ruby activerecord ActiveRecord::Rollbackで例外を伝播させずにロールバック後の処理を継続する @wakairo 2026/01/18 22:49 Active RecordのTransactionブロック内で例外が投げられた場合、ActiveRecord::Rollback以外の例外はロールバックの後に再度投げられます。 したがって、処理失敗で例外を投げるメソッド(save!等)を使えば、基本的にその例外はロールバックの後Transactionブロックの外へ伝播します。 これをキャッチしなければ、RailsはHTTPのエラーレスポンスをブラウザ等のクライアントに返します。 つまり、「処理に失敗したら、ロールバックして、あとの処理は切り上げて、エラーレスポンスを返す」という挙動は簡単に実装できます。 一方で、「ロールバック後に例外を出さずに処理を継続する」場合には、ActiveRecord::Rollbackを活用できます。 以下は、Transactionの成否でリダイレクト先を変えるコード例です。 completed = false ActiveRecord::Base.transaction do unless obj1.save && obj2.save raise ActiveRecord::Rollback end completed = true end if completed redirect_to completed_path else redirect_to uncompleted_path end
Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業
Rails 8.1におけるRails.configuration.action_view.remove_hidden_field_autocomplete
config.action_view.remove_hidden_field_autocompleteは、hiddenフィールドからautocomplete="off"属性を除去するかどうかの設定です。 (この機能のPRとこの機能のCommit)bin/rails app:updateコマンドが生成する config/initializers/new_framework_defaults_8_1.rbには、 以下のようにこの設定を有効にするコードがあります。この機能が導入された背景
この機能は、HTML標準へ より準拠したHTMLをRailsが生成するように追加されました。
具体的には、この機能を有効にしない場合、Railsは以下のような
autocomplete="off"属性を付けたhiddenフィールドを生成していました。このautocomplete属性付きのhiddenフィールドはHTML標準に沿っておらず、 Nu Html Checkerは 以下のメッセージとともに「エラー」として指摘していました。
Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業
Rails 8.1におけるconfig.action_dispatch.verbose_redirect_logs
config.action_dispatch.verbose_redirect_logsは、リダイレクトのソース位置を関連するログ行の下にログ出力するかどうかの設定です。rails newで生成されるconfig/environments/development.rbにおいて、 8.1からは以下のようにこの設定を有効にするコードが追加されました(この変更のPRとこの変更のCommit)。development環境でデバッグする際に使える情報が増える形になると思いますので、既存アプリでもこの設定を追加しても良いかもしれません。
Rails 8.0からRails 8.1への移行(アップデート、アップグレード)で必要な作業
基本的にはRailsガイドの手順に従えば良いと思います。
ガイドの手順にもありますが、ぜひ
bin/rails app:updateコマンドを活用しましょう。Rails 8.1への移行で対応が必要そうな個別の作業について、以下のコメントでそれぞれ取り上げますので、ご参考になれば幸いです。
Active Recordのgenerates_token_for:DBカラム不要で一時的なトークンを扱う機能
generates_token_forとは
generates_token_forは、特定の目的を持つトークンを生成し、そのトークンからレコードを検索・検証するための機能です。 Rails 7.1で標準機能として導入されました。特長
使いどころ:一時的に有効なURLの送付
以下のような「一時的で目的が限定されたトークン付きURL」を送付する場面に適しています。
なお、APIの認証トークンのような永続的な用途には向きません。 Railsで永続的なトークンを扱いたい場合は、
has_secure_tokenなどの利用を検討してください。注意:パスワードリセットには専用APIあり
Rails 8.0以降で
has_secure_passwordを利用している場合、パスワードリセット用トークンを扱う専用APIが自動的に提供されます。そのため、パスワードリセットの用途に限っては、自分でgenerates_token_forを定義する必要はありません。(詳しくは後述の補足を参照してください)使い方(メールアドレス確認の例)
ここでは「メールアドレス変更時の確認リンク」を題材に、基本的な使い方を説明します。
基本の流れ(定義・生成・検証)
まず、モデルに
generates_token_forを用いて、トークンの用途名(ここでは:email_verification)を定義します。 なお、用途名が異なっていれば複数定義することも可能です。トークンを生成する際は、対象のインスタンスに対して
generate_token_forを呼び出します。生成したトークンは、例えば以下のようにURLパラメータに載せてメールなどで送付するのが典型的な流れです。
受け取ったトークンからレコードを検索・検証するには、クラスメソッドの
find_by_token_forを使います。 有効なトークンならUserインスタンスが返り、無効なトークンや期限切れのトークンの場合はnilが返ります。有効期限による失効
定義時に
expires_inオプションを付与することで、トークンに有効期限(例:15分間)を設けることができます。モデルのデータ変化による失効
generates_token_forは、「一度状態が変わったら、古いトークンを使わせない」挙動を簡単に実装できます。 例えば、新しいメールアドレス確認用に生成されたトークンを、メールアドレスの変更が完了した後に無効化すれば、万が一流出しても悪用を防げます。無効化の仕組みは「定義時のブロックの戻り値をトークンに埋め込み、検証時にその値が変わっていれば無効とみなす」というものです。
注意:ブロックの戻り値に機密情報を含めない
ブロック内で返した値は、改ざん耐性はあるものの、暗号化されるわけではなく、デコードすれば読める形でトークンに埋め込まれます。 そのため、ブロックの戻り値には機密情報を含めてはなりません。
補足:パスワードリセット専用API
Rails 8.0以降では、モデルで
has_secure_passwordを利用している場合、パスワードリセットトークン用の以下のメソッドがデフォルトで追加されます。password_reset_token:トークン生成(インスタンスメソッド)find_by_password_reset_token:トークンからの検索と検証(クラスメソッド)find_by_password_reset_token!:同上(無効時に例外を発生させるバージョン)Rails 8.1で追加されたトークン有効期限の機能
さらにRails 8.1以降では、トークンの有効期限を柔軟に扱うための機能が追加されています。
has_secure_passwordの呼び出し時にreset_tokenオプションを通して有効期限を変更できるようになりました。また、以下のメソッドが追加されました。
password_reset_token_expires_in:設定されている有効期限の取得(インスタンスメソッド)参考情報
Railsモデルのクラスメソッドはメソッドチェーンでも呼べる ― 注意点とscopeへ整理する方針
Railsでモデルにクラスメソッドを定義すると、メソッドの内容に関わらず、メソッドチェーン(ActiveRecord::Relation)経由でもそのメソッドを呼び出せます。 これが便利な場合がある一方で、エラーにはならないものの、意味的に不正確なコードが書けてしまったり、気を付けないとバグの温床になったりすることもあります。 特に、リレーションを返す用途のクラスメソッドと、そうでないクラスメソッドが混在している場合に混乱が生じやすくなります。 本記事ではその仕組みと注意点、そして実務上の整理方針を解説します。
モデルのクラスメソッドがActiveRecord::Relation経由でも呼べる仕組み
以下のようにPenクラスにクラスメソッドを定義したとします。
定義したこのメソッドは以下のように呼べます。これはRubyにおける通常のクラスメソッド呼び出しです。
では、メソッドチェーンでの呼び出しはどうなっているでしょうか。
まず、
whereなどのスコープメソッドを呼び出した結果は、以下で確認できる通り、 PenクラスではなくPen::ActiveRecord_Relationのインスタンスになります。このリレーションインスタンスに対しても、クラスメソッドを以下のように呼び出せます。 つまり、モデルに定義したクラスメソッドは、そのモデルに対応したActiveRecord::Relationクラスのインスタンスからも呼べる仕組みになっているのです。この仕組みが、クラスメソッドをメソッドチェーンから呼べるようにしています。
メソッドチェーンで呼べてしまうからこそ、モデルのクラスメソッドは責務と名前を明確にする
with_colorのようにクエリを組み立てるメソッドであれば、メソッドチェーンで呼び出せても違和感がないと思います。 しかし、データベースクエリと関係のないメソッドでもクラスメソッドであれば同様にリレーション経由で呼び出せてしまいます。その結果、クラスメソッドであれば、その処理内容に関わらず、クエリを組み立てるメソッドを間に挟んだメソッドチェーンにおいて、エラーにならず実行できてしまいます。 具体例を見てみましょう。
export_to_csvがクラスメソッドだとすると以下のようにwhereを間に挟んでもエラーにならずに実行できます。では
export_to_csvメソッドは、countのように「条件に合ったレコードに対する処理をするメソッド」なのでしょうか? それとも、クエリ構築とは関係がない「ユーティリティメソッド」で、このメソッドチェーンで呼び出すコードは何かしらのミスの結果なのでしょうか?この例から分かるのは、モデルクラスに定義するクラスメソッドの名前はより慎重に付けなくてはならないということです。 名前だけではありません。メソッドの責務についても、クエリ用途であるか否かなど、明確さがあることが望まれます。
そのため、モデルのクラスメソッドの責務と名前に一定の規則性を持たせることも1つのアイデアです。 例えば、「
findやcreateなど、Active Recordのクエリメソッドで使われる単語から名前が始まる場合は、必ずクエリ関連の責務とする」といったルールが考えられます。また、名前だけでなくコードドキュメントにおいても、他のメソッド以上に使い方をはっきりと示すことが望まれます。
クエリ構築用クラスメソッドでelse節を省くとメソッドチェーンの途中でエラーを招く
クエリ構築用のクラスメソッドの場合には、条件分岐の
else節を省いてしまうと、メソッドチェーンの途中で予期せぬエラーを引き起こす原因になるという注意点もあります。以下のように、先ほどの
with_colorメソッドからelse節を削除してみましょう。引数が有効値のとき(
color.present?が真になるとき)はエラーになりませんが、 以下のように引数が空文字列で条件を満たさない場合はNoMethodErrorが発生してしまいます。これは、以下のように条件を満たさなかった場合の戻り値が
nilになってしまうためです。nilに対してcountメソッドを呼び出そうとしたため、エラーになったのです。クエリ構築用メソッドとしてクラスメソッドを設計するなら、 常にActiveRecord::Relationインスタンス(またはそれに準ずるオブジェクト)を返す設計にしておくことが求められます。
リレーションを返す場合はscopeに統一する
クエリ構築の一環としてリレーションを返すことを意図したメソッドは、 クラスメソッドではなくscopeでの定義に統一するのが有効です。 これは、前述したモデルのクラスメソッドに関する困難さを和らげる、シンプルなアプローチの1つです。
1つのプロジェクト内でクラスメソッドでの定義とscopeでの定義を混在させることは、 前述のクラスメソッドの注意点とscopeの注意点を足し合わせることになり、チーム開発における理解コストを上げてしまいます。 では、混在をやめてどちらに統一するのが良いかと言えば、scopeに統一することが有力な戦略です。以下にその理由を3つ述べます。
理由1: scopeとクラスメソッドでは挙動に違いがある
まず大きな違いは、
nilまたはfalseを返したときの挙動です(この点については理由3で詳しく後述します)。 また、レシーバとしてモデル名を書いた際にも明確な挙動の違いが確認できます(下記「参考」を参照)。 これらに限らず、他の挙動の差異が潜んでいる可能性もあります。このように仕様や挙動に差異がある場合、二通りのやり方が混在していると「片方で問題ない書き方がもう片方でバグになる」という厄介さを抱えることになります。また、両方の仕様・挙動に対する知識が求められるため、コードベースの理解コストも上がってしまいます。
(参考)モデルクラス名を明示的にレシーバとした場合の挙動の違い
クエリ構築を行うscopeとクラスメソッドでは、モデルクラス名を明示的にレシーバとして書いてしまうと、 前の条件を引き継ぐか否かに違いが出てしまいます。
具体的には、まず以下のように、
Pen.where(color: color)とだけ記述したscopeとクラスメソッドをそれぞれ定義します。次に、それぞれを実行してみると、以下のような違いが出ます。
クラスメソッドの
with_color_mは前の条件where(price: 80)が適切にマージされているのに対し、 scopeのwith_color_sはprice: 80の条件が無視されています(where(color: color)のようにレシーバPenを消せば正しくマージされます)。なお、この違いの確認には執筆時の最新版であるRails 8.1.2を用いました。
理由2: scopeには「リレーションを返す」という共通認識がある
クラスメソッドは、用途が極めて広い汎用的な仕組みです。そのため、メソッド名だけでは「チェーン可能なクエリメソッドなのか」が判断しづらくなります。
一方、scopeは「クエリを組み立ててActiveRecord::Relationを返すためのもの」という開発者間の共通認識があります。 また、scopeは通常のクラスメソッド定義方法とは異なるため、メソッドチェーンで利用できることも自然に受け入れられます。
以上から、scopeの方が、コードの読み書きに必要な認知的負荷が低いと言えます。
理由3: scopeでは、else節を省いた簡潔な記載が可能
RailsのAPIドキュメントによれば、
nilまたはfalseが返された場合にその時点までに構築済みのクエリ条件(allに相当)を返すというのがscopeの仕様です。そのため、以下のscope定義にはelse節がありませんが、メソッドチェーンの途中で呼び出されてもエラーを引き起こしません。
これにより、先ほど述べたクラスメソッドが持つ「nilを返してチェーンが壊れる」問題を回避しつつ、条件付きクエリを簡潔に表現できます。
さらにRubyでは、全く同じ処理を以下の一行に書き直せます。
このようにscopeを使うことで、意図が明確かつ簡潔なコードを書くことが出来ます。
まとめ
nil返却エラーを防いだり、コードの読み書きを容易にしたり出来ます。Active Recordのallが持つ条件を引き継ぐ性質と動的クエリ構築への応用
RailsのActive Recordの
allメソッドは、 その名前から「テーブルに存在する全レコードを取得するメソッド」と理解されがちです。 しかし実際には「その時点までに積み上げられた条件に該当するものを全て取得する」という実務上とても重要な性質を持っています。本記事では、この
allの性質の基本と、動的にデータベースクエリを組み立てる際の実践的な応用テクニックについて解説します。allは「構築済みの条件」を引き継ぐデータベースに
Penモデルのレコードが3件保存されているとします。 以下のようにPenクラスに対して直接allを呼び出すと、テーブルに存在する全てのレコードが取得されます。では、別の条件に続けて
allを呼び出すとどうなるでしょうか。 以下の実行例を見ると分かるように、allは「全て」ではなく、「それまでの条件を満たすもの全て」を返します。 ここではcolor: "red"を満たすレコードだけが取得されています。このように、
allは単に全件取得する機能だけでなく、 手前に条件がある場合には「そこまでに構築されたクエリ条件(Relationオブジェクト)を引き継ぎ、そのまま返す」という機能を担う側面があります。応用例:動的クエリ構築において
allを起点にしてメソッドチェーン可能にするこの「そこまでに構築された条件を引き継ぐ」という
allの性質は、 「URLのクエリパラメータが存在する場合だけ条件を追加する」といった動的なデータベースクエリ構築において役立ちます。例えば、クエリパラメータを処理する
filter_byというスコープを定義することを考えてみましょう。 このスコープは、以下のように「1年以内に作成されたレコード」という前提条件(where)のあとにメソッドチェーンで呼び出される想定です。この挙動を実現する
filter_byスコープの実装は以下のようになります。 最初にallを呼び出して現在のRelation(既に構築済みのwhere条件など)を変数pensに格納し、 そのpensに対してパラメータの有無をチェックしながら条件を継ぎ足しています。ここで重要になるのが、
filter_byの中で最初にallを呼んで変数に入れている点です。allは「何も条件がない状態」ではなく、 「このスコープが呼ばれた時点のRelationをそのまま受け取る」役割を果たしています。 そのため、Pen.where(created_at: …)のように手前で条件を付けていても、 それをリセットすることなくさらに条件を積み上げられます。 つまり、allを変数に入れることで、 手前の条件を維持した上でさらに条件を積み上げていくための「起点」を作ることができるのです。 なお、クエリパラメータが全て空で条件追加がない場合でも、allが返した元のRelationがpensに入っているため、 メソッドチェーンをそのまま継続できます。(参考)個別スコープへの切り出しとそのタイミング
先ほどの例では
filter_byの内部で変数に再代入しながら条件を追加していましたが、 もしcolorやpriceによる絞り込みをアプリケーション内の他の場所で単独で利用したい場合は、 それぞれを個別のスコープとして切り出すことでコードをよりキレイに書くことができます。Active Recordのスコープは、ブロックの評価結果が
nilまたはfalseになった場合、 自動的に元のRelationをそのまま返す(条件を追加せずにスルーする)という便利な仕様になっています。 そのため、以下のようにメソッドチェーンでそのままつなぐだけで、条件分岐をスコープの中に自然にカプセル化できます。なお、YAGNI原則の観点からは、 これらの個別スコープ(
with_colorやwith_max_price)が他の場所で「本当に必要になるまで」は、 無理に切り出さない方が良いでしょう。 つまり、最初から再利用性を過剰に意識してスコープを量産するよりは、 まずは前述のpens = allを使うアプローチで1箇所にまとめておき、 個別スコープが本当に必要になったタイミングで切り出す方が、 結果的には開発コストを減らせるはずです。RailsのAction MailerとAction Mailboxはmail gemを利用している
RailsでMailクラスに遭遇したら、それはmail gemのものです。ですので、そのAPI情報などは、Railsの情報源ではなく、以下のようなmail gemの情報源を見ると得られます。
Active Recordマイグレーション:t.referencesとadd_referenceで外部キー制約を詳細設定する方法
要点
Active Recordのマイグレーションファイルにて
t.referencesまたはadd_referenceを使う際、foreign_key:オプションに対してtrueの代わりに ハッシュを渡すことで外部キー制約の詳細を設定 できます。このハッシュで指定できるオプションはadd_foreign_keyのAPIドキュメントに記載されているオプションです。
具体例
t.referencesを使う場合(新規テーブル作成時)add_referenceを使う場合(既存テーブルへの追加)説明
Active Recordのマイグレーションファイルにてテーブル間に関連付けをする際には、
t.referencesまたはadd_referenceを使うと便利です。この2つのメソッドは、呼び出すときに
foreign_key:オプションを付けると、 外部キー制約も同時に付与することが出来ます。例えば、foreign_key: trueという記述をよく見かけます。foreign_key: trueは外部キー制約をかけることだけの指定となりますが、 ここでのtrueの代わりにハッシュを渡すことで、外部キー制約の詳細が設定可能になります。このハッシュの中に入れられるオプション、つまり外部キー制約の詳細設定項目は、
add_foreign_keyに渡せるオプションと同じです。 このadd_foreign_keyに渡せるオプションの具体的な種類とそれぞれの意味については、 add_foreign_keyのAPIドキュメントをご覧ください。Active Recordマイグレーションでのadd_foreign_keyとadd_referenceの違い
Railsガイドを一読しただけでは、 add_foreign_keyとadd_referenceが、それぞれどのようなもので、どう違うのかがいまいちよく分かりませんでした。 そこで、add_foreign_keyとadd_referenceによって
db/schema.rbがどのように変化するかを基に、 それぞれの機能について確認してみました。add_foreign_key
add_foreign_keyは外部キー制約の追加だけを行います。よって、カラムやインデックスの追加は行いません。
外部キー制約とは、子テーブルが参照している親のIDが親テーブルに存在することをデータベースレベルで保証する制約です。
例えば、以下の記述をマイグレーションファイルに行ったとします。
すると以下の記述が
db/schema.rbに追加されます。この記述は、products(子テーブル)のuser_idカラムに登場するIDが、users(親テーブル)のidカラムに必ず存在するように制約をかけています。
add_reference
add_referenceは、テーブル間の関連付けに関する複数の設定を一度に行える便利メソッドです。
add_referenceの基本機能は、カラムとインデックスの追加です。 例えば、以下の記述をマイグレーションファイルに行ったとします。
すると以下のように、users(親テーブル)に関連付ける2つの記述、具体的にはuser_idのカラムとインデックスをproducts(子テーブル)に追加する2つの記述が、
db/schema.rbに追加されます。またadd_referenceは、
foreign_key:trueオプションを追加することで、前述の2つに加えて外部キー制約も同時に設定できます。 例えば、以下の記述をマイグレーションファイルに行ったとします。すると以下のように、前述の2つの記述に加えて、add_foreign_keyの記述が
db/schema.rbに追加されます。(参考)add_referenceとt.referencesの機能は基本的に同じ
add_referenceとcreate_tableのブロックの中で呼び出すt.referencesは基本的に同じ機能を提供します(オプション以外の引数の部分で違いはありますが。) 実際にt.referencesへ渡せるオプションはadd_referenceと同じです。 また、t.referencesの実装とadd_referenceの実装のどちらも
ReferenceDefinition.newを内部で呼ぶ形になっています(v8.1.2で確認)。ActiveRecord::Rollbackで例外を伝播させずにロールバック後の処理を継続する
Active RecordのTransactionブロック内で例外が投げられた場合、ActiveRecord::Rollback以外の例外はロールバックの後に再度投げられます。
したがって、処理失敗で例外を投げるメソッド(
save!等)を使えば、基本的にその例外はロールバックの後Transactionブロックの外へ伝播します。 これをキャッチしなければ、RailsはHTTPのエラーレスポンスをブラウザ等のクライアントに返します。 つまり、「処理に失敗したら、ロールバックして、あとの処理は切り上げて、エラーレスポンスを返す」という挙動は簡単に実装できます。一方で、「ロールバック後に例外を出さずに処理を継続する」場合には、ActiveRecord::Rollbackを活用できます。 以下は、Transactionの成否でリダイレクト先を変えるコード例です。