はじめに
こんにちは、Webエンジニア目指して学習中のさばと申します🐟(X:@saba7678pg)
昨夜戦っていたエラーとの痕跡を折角なので記録しておこうと思います。
もっとこうした方が良い、この方法だと不都合が起きる等
何かございましたら、お手数ですがコメント・DM等でお知らせ頂けますと幸いです。
環境
- Docker環境下
- ruby 3.2.2
- rails 7.1.2
- rails-i18n 7.0.8
フラッシュメッセージが"一部"正しく翻訳されない
以下画像のように、フラッシュメッセージが一部だけ翻訳されない不具合が出ておりました。
# views/posts/_form.html.erb
<%= form_with model: post, local: true, class: "space-y-4" do |f| %>
<%= render 'shared/error_messages', model: f.object %>
<!-- 都道府県のプルダウンメニュー -->
<div>
<%= f.label :prefecture_id, class: "block text-sm font-medium text-gray-700" %>
<%= f.collection_select :prefecture_id, Prefecture.all, :id, :name, {prompt: "選択必須"}, {class: "select select-bordered select-sm w-full max-w-xs"} %>
</div>
<!-- 観光地名のテキストフィールド -->
<div>
<%= f.label :location, class: "block text-sm font-medium text-gray-700" %>
<%= f.text_field :location, placeholder: "観光地名やスポット名を記入してください", class: "input input-bordered input-sm w-full max-w-xs" %>
</div>
以下省略
# views/shared/_error_messages.html.erb
<% if model.errors.any? %>
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4" role="alert">
<ul>
<% model.errors.full_messages.each do |message| %>
<li class="list-disc list-inside"><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
以下、私の対応工程・上手くいかなかった点がつらつら書いてありますので、
結論を知りたい方は目次の解決の兆し・対応方法へ飛んでくださいませ🙇♂️🙇♂️
対応①:ja.yml
の確認
そもそもja.ymlが正しく記載されているか?を確認
irb(main):001> I18n.t('activerecord.attributes.post.prefecture_id')
=> "都道府県"
irb(main):004> I18n.t('activerecord.models.prefecture')
=> "都道府県"
問題なく設定できているように思えます。
対応②:そもそもフラッシュメッセージの中身がどうなってるかを確認してみる
$ rails c
irb(main):001> post = Post.new
=>
#<Post:0x0000ffffb2b583c8
...
irb(main):002> post.valid?
=> false
irb(main):003> post.errors.full_messages
=> ["Userを入力してください", "Prefectureを入力してください", "観光地・スポットを入力してください", "旅行の思い出を入力してください"]
ログインしているユーザーのみが投稿機能を使えるので、そもそもUserが空になるという状態は起きえないのですが、
ここでPrefecture同様Userも翻訳できていないことが分かりました。
どうやらform_with
で関連づけているモデルがPostなので、
Postと関係を持っている他テーブルの情報取得の際に翻訳エラーが起きているようです。
(今回はPostモデルにUserとPrefectureがそれぞれ結びついています)
対応③:モデルを明示して記載してみる
# posts/_form.html.erb
- <%= f.label :prefecture_id, class: "block text-sm font-medium text-gray-700" %>
+ <%= f.label :prefecture_id, Post.human_attribute_name("prefecture_id"), class: "block text-sm font-medium text-gray-700" %>
or
Prefecture.human_attribute_name("name")
ラベルは置き換わるけれど、出ているフラッシュメッセージのPrefectureは変化がないので呼び出し方自体に問題があるのか…?
解決の兆し①:postモデルにprefecture_idのバリデーションをかけていない
そもそもpostsのprefecture_idにはバリデーションをかけておらず、
prefecturesテーブルでnull: falseを設定していました。
# schema.rb
create_table "posts", force: :cascade do |t|
t.bigint "user_id"
t.bigint "prefecture_id"
t.string "text", null: false
t.index ["prefecture_id"], name: "index_posts_on_prefecture_id"
t.index ["user_id"], name: "index_posts_on_user_id"
end
create_table "prefectures", force: :cascade do |t|
t.string "name", null: false
t.index ["name"], name: "index_prefectures_on_name", unique: true
end
# models/post.rb
class Post < ApplicationRecord
belongs_to :user
belongs_to :prefecture
以下省略
試しにvalidates :prefecture_id, presence: true
を追記してみると
正しく翻訳されたフラッシュメッセージが出てくるようになりました!
(ちなみにPrefecture・都道府県を入力してくださいと2つのエラーが出ていますが、
正しく都道府県を入力した状態にするとどちらのエラーも消えました)
となると、Prefectureはどこから来てるんだ…?
解決の兆し②:belongs_to
によるバリデーションを緩和する
Rails5以降、belongs_to
関連付けの際にrequired: true
がデフォルト設定されているようです。
関連するprefecture
が存在しない場合、Postオブジェクトのバリデーションに失敗する、
フラッシュメッセージに出てきていたPrefecture
はここから来ていたのでしょうか。
この挙動をbelongs_to :prefecture, optional: true
とし、バリデーションを無効にすることで
「Prefectureを入力してください」というフラッシュメッセージは出なくなりました👏
対応方法:models/post.rb
のバリデーション変更
class Post < ApplicationRecord
belongs_to :user
- belongs_to :prefecture
+ belongs_to :prefecture, optional: true
+ validates :prefecture_id, presence: true
#<Post:0x0000ffff9a849258
id: 3,
user_id: 1,
location: "test",
prefecture_id: 1,
以下省略
最後に
GPT「i18nのインデントを確認してみて!」
🐟「インデントも問題ないよ!」
GPT「OK、それじゃあ今度はインデントを確認してみて!」
🎣「合っとるだろうがよおᗦ↞◅」
不毛なやり取りを続けておりました。
引き続き個人開発で遭遇したエラーで学びがあれば記事に書いていこうと思います。
ここまで読んでいただきありがとうございました🐟