【Rails】associationを張ったModelを生成する時のmigrationの記述
一人のユーザは一つのプロフィールを有する、というようなAssociationをつくってみる
1:1です
前提
以下のような構成を目指す
User
- name
- profile_id
Profile
- text
モデルはこうなる
class User < ApplicationRecord belongs_to :profile end class Profile < ApplicationRecord end
こんな感じのことをしたい
p = Profile.new(text: 'hello') p.save! u = User.new(name: 'test user', profile: p) u.save! User.first.profile.name # 'hello'
Profileの生成は特に迷うことはない
rails g model profile text:string
db/migrate/yyyyMMddHHmmss_create_profile.rb
class CreateProfiles < ActiveRecord::Migration[5.2] def change create_table :profiles do |t| t.string :text t.timestamps end end end
で、Userを作っていく時に色々間違えたのでそのメモ
結論からいうと...
上記の
User
- name
- profile_id
こんな感じにUserにprofile_idを持たせてあげて、Modelにbelongs_to :profile
の記述するだけではダメで、
schema.rbにもbelongs_toが記述されていないと、正しくActiveRecordのassociationが効いてくれない
正しいパターン
rails g model user name:string profile:belongs_to
※belongs_toはreferenceでもよい
参考:
belongs_to
is an alias ofreference
https://stackoverflow.com/questions/7788188/what-is-the-difference-between-t-belongs-to-and-t-references-in-rails
このgenerateコマンドによって上記のModelと以下のmigrationファイルが生成される
db/migrate/yyyyMMddHHmmss_create_user.rb
class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| # ↓が必要な行 # ついでにprofilesじゃなくて単数形のprofileであることにも注意 t.belongs_to :profile, foreign_key: true t.string :name t.timestamps end end end
:nameがt.string
、つまりstring型であるように
:profileは reference型 という型になる (belongs_to型とはあんまり言わない)
このreference型の:profileが、カラムprofile_idを生成してくれる
メモ:
ソース的には
ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference のあたり
add_reference(:table_name, :reference_name):
Adds a new column reference_name_id by default an integer.
ややこしい話はさておき、Migrationを適用するとこうなる
schema.rb
ActiveRecord::Schema.define(version: yyyy_MM_dd_HHmmss) do create_table "profiles", force: :cascade do |t| t.string "text" t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| t.integer "profile_id" t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["profile_id"], name: "index_users_on_profile_id" # ここがポイント! end end
勘違いしてた失敗パターン
こんな感じただUserにprofile_idを持たせてあげて、Modelに
belongs_to :profile
の記述するだけではダメで、 schema.rbにもbelongs_toが記述されていないと、正しくActiveRecordのassociationが効いてくれない
これをやってしまったパターン
rails g model user name:string profile_id:integer # 正しいのは↓ # rails g model cat name:string profile:belongs_to
db/migrate/yyyyMMddHHmmss_create_user.rb
class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name t.integer :profile_id # 正しいのは↓ # t.belongs_to :profile, foreign_key: true t.timestamps end end end
Migrationを適用するとこうなる
schema.rb
ActiveRecord::Schema.define(version: yyyy_MM_dd_HHmmss) do create_table "profiles", force: :cascade do |t| t.string "text" t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| t.integer "profile_id" t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false # ↓が足りてない # t.index ["profile_id"], name: "index_users_on_profile_id"0 end end