はじめに
ActiveRecordsについてあまり理解していないので、今回深掘りしてみました。
Active RecordとはRailsにおけるObject/Relational Mapping (ORM) です。
※訳:オブジェクト関係マッピング
Object/Relational Mapping (ORM)とは?🤔
以下Wekipediaより
オブジェクト関係マッピング(英: Object-relational mapping、O/RM、ORM)とは、データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である
つまり、RubyでDBを操作するための技法ということですね!!👏
RailsアプリでDBに変更を加えるとき、Rubyのコードで直感的にDBを操作することができるのもActive Record様のおかげ
また、ActiveRecord様のおかげでDBの種類(MySQL, PostgreSQL等)に関わらず同じコードで操作可能になります!
Active Recordはどのように機能している?
では、お馴染みのrails g modelをして、DBにテーブルを追加して行きます!
rails g model Post title:string
create db/migrate/20220826_create_post.rb
create app/models/post.rb
このファイルはどこから来ているのでしょうか?👀
これはrails g modelコマンドを実行時にrailsのmodel_generator.rbが呼び出されています!
コードを確認してみます!
....
# creates the migration file for the model.
def create_migration_file
return if skip_migration_creation?
attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
end
def create_model_file
generate_abstract_class if database && !custom_parent?
template "model.rb", File.join("app/models", class_path, "#{file_name}.rb")
end
....
1つ目のメソッドはマイグレーションファイル、
2つ目のメソッドはモデルファイルを生成していますね!!
下記の記述をさら見てみます
migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
def change
create_table :<%= table_name %><%= primary_key_type %> do |t|
<% attributes.each do |attribute| -%>
<% if attribute.password_digest? -%>
t.string :password_digest<%= attribute.inject_options %>
<% elsif attribute.token? -%>
t.string :<%= attribute.name %><%= attribute.inject_options %>
<% elsif attribute.reference? -%>
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %>
<% elsif !attribute.virtual? -%>
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
<% end -%>
<% end -%>
<% if options[:timestamps] %>
t.timestamps
<% end -%>
end
<% attributes.select(&:token?).each do |attribute| -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
<% end -%>
<% attributes_with_index.each do |attribute| -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<% end -%>
end
end
なんかみたことある形....
これは、、
rails g modelのときに生成されるマイグレーションファイルと同じフォーマットですね!!!
class CreatePost < ActiveRecord::Migration
def change
create_table :post do |t|
t.integer :title
t.timestamps
end
end
end
ということは、rails g model実行時に渡したモデル名やカラムが
ファイルを生成時に使われていることがわかりますね!
次にrailsdb:migrateを実行し、DBにテーブルを作ります
ということは、rails g model実行時に渡したモデル名やカラムが
ファイルを生成時に使われていることがわかりますね!
次にrailsdb:migrateを実行し、DBにテーブルを作ります。
では、データを入れて行きましょう!
post = Post.new title: "This is title"
Postのインスタンスを生成してみます。
次にDBに保存するために、post.save
をします。
INSERT INTO "post" ("title", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["title", "This is title"], ["created_at", Fri, 26 Aug 2022 18:20:12 UTC +00:00], ["updated_at", Fri, 26 Aug 2022 18:20:12 UTC +00:00]]
DBに保存時このメソッドを使用しています。
# Executes an INSERT query and returns the new record's ID
#
# +id_value+ will be returned unless the value is +nil+, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
sql, binds = to_sql_and_binds(arel, binds)
value = exec_insert(sql, name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end
まとめ
Active Recordのおかげで、直感的にDB操作することが可能になることがわかりました。
Active Recordに関わらず、Railsはあまり深い理解をせずとも動いてくれるので、
裏でどのようになっているかはしっかりと理解する必要があると感じました。
今後もどのようなロジックで動いているのかしっかり調べていこうと思いました。