💎
【ActiveRecord】結合したテーブルに同名カラムがある場合 select('*') を使用すると違うモデルにマッピングされる
はじめまして、おてつたび の川尻です。
去年見つけて投稿したIssueがPR作成されて進んでいたので、紹介します。
そもそもの Issue はこちら。(タイトル名が合ってない気もしますが)
Issue にも記載した例で解説します。
例
著者(authors)と記事(articles)テーブルがあり、author_id で紐づいているとします。
モデルもそれぞれ Author と Article があります。
authors
| id | name |
|---|---|
| 1 | John |
| 2 | Smith |
articles
| id | author_id | name |
|---|---|---|
| 1 | 2 | Hello World |
この時結合して select('*') を使用すると、Article に authors のレコードの値がアサインされます。
外部結合(left_joins, eager_load)も同じ結果になります。
article = Article.joins(:author).select('*').find 1
# => SELECT * FROM "articles" INNER JOIN "authors" ON "authors"."id" = "articles"."author_id" WHERE "articles"."id" = 1 LIMIT 1
article.id
# => 2
# 1 が正しい
article.name
# => Smith
# Hello World が正しい
テーブル名を付けると正しくマッピングされます。
Article.joins(:author).select('articles.*', 'authors.*')
つまり、テーブル名が無いと同名カラムをどのテーブルのモデルにマッピングするか Arel が判断できないので間違ったモデルにアサインされてしまうのです。
回避も SELECT * が原因なので、テーブル名をつけて「*」を使用する(select('articles.*))、もしくはカラムを指定する(select(:id, :name))になります。
今後
結合したテーブルと同名カラムがある場合はWarningメッセージを出力するように対応してくれています。
「*」だけで指定するケースはあまり無いと思いますが、使用する際はご注意ください!
終わりに
株式会社おてつたびでは、一緒に働く仲間を募集しています!
WebとiOSエンジニアを募集しております。
募集が見つからない場合は直接ご連絡していただいても大丈夫です!
Discussion