自宅のWindowsでRuby on Rails ~データベース操作1

Webアプリケーションにおいて、データベースは非常に重要な役割を果たしています。


多くのWebアプリケーションは、ユーザーからの入力情報を記憶するためにデータベースを利用しています。

データベースがなければ、Webアプリケーションは単なるツールにすぎず、ユーザーの利便性を向上させることはできません。

また、Webアプリケーションの開発者にとっても、データベースは欠かせない存在です。

なぜなら、データベースを利用することで、Webアプリケーションの機能性や安定性を高めることができるからです。


この記事では、データベースがWebアプリケーションにおいて果たす役割や、RailsでWebアプリケーションを開発する上でのデータベースの設計や操作について、具体的な例を交えながら解説していきます。

1. もしデータベースがなかったら?


Webアプリケーションにデータベースがなかった場合、以下のような問題が発生する可能性があります。

Webアプリケーションにデータベースがない場合、ユーザー体験やデータの整合性、セキュリティ、アプリケーションの拡張性に悪影響を与える可能性があるため、データベースの利用は非常に重要です。

1-1. データの永続性がない

Webアプリケーションがデータベースを利用しない場合、ユーザーから入力された情報やWebアプリケーションで生成された情報は、メモリ上にしか保持されず、アプリケーションが終了するとすべて失われます。

このため、アプリケーションを再度利用するたびに、ユーザーは同じ情報を再入力する必要があります。

1-2. データの整合性が担保されない

Webアプリケーションがデータベースを利用しない場合、複数のユーザーが同時にアプリケーションを利用している場合、それぞれが異なる情報を入力してしまう可能性があります。

その結果、データが整合性を欠くことがあります。

1-3. データのセキュリティが脆弱になる

Webアプリケーションがデータベースを利用しない場合、ユーザーが入力した情報がWebアプリケーションのコードに直接埋め込まれたり、ファイルに保存されたりすることがあります。

このため、情報漏洩や改ざんのリスクが高まります。

1-4. アプリケーションの拡張性が低下する

Webアプリケーションがデータベースを利用しない場合、アプリケーションの機能追加や改修が難しくなります。

なぜなら、新しい機能を実装するためには、アプリケーションのコードを書き換える必要があり、既存のデータに影響を与える可能性があるためです。

2. Active Record


Ruby on Railsにおけるデータベース操作には、「Active Record」という機能があります。

Ruby on Railsは、MVC(Model-View-Controller)アーキテクチャを採用しており、Active Recordは、そのうちの1つのコンポーネントであるModelに含まれます。

Active Recordは、データベースとのやり取りを行うオブジェクト関係マッピング(ORM)ライブラリであり、Railsアプリケーションでのデータベース操作を抽象化して簡単に扱えるようにするために用意されています。

Active Recordは、データベースに対するクエリを直接書く必要がなく、オブジェクト指向プログラミングの考え方に基づいたコードを記述するだけで、データベースにアクセスできます。

これにより、開発者はより効率的かつ生産的に、Ruby on Railsアプリケーションを開発することができます。

3. ActiveRecordの基本操作

ActiveRecordを使うためには、以下の手順が必要です。

3-1. モデルの作成

ActiveRecordを使用するには、モデルが必要です。

モデルは、アプリケーション内のデータベーステーブルと1対1に対応します。

モデルを作成するには、ターミナルで以下のコマンドを実行します。

rails g model ModelName

このコマンドを実行すると、app/modelsディレクトリにモデルのスケルトンが生成されます。

3-2. マイグレーションの作成

次に、データベースに必要なテーブルを作成するためのマイグレーションファイルを作成する必要があります。

マイグレーションファイルは、データベースのスキーマを変更するためのRubyファイルです。

以下のコマンドを使用して、新しいマイグレーションを作成できます。

rails g migration MigrationName

このコマンドを実行すると、db/migrateディレクトリに新しいマイグレーションファイルが生成されます。

このファイルには、データベーススキーマの変更を定義するコードを追加する必要があります。

3-3. マイグレーションファイルの編集

2.で作成されたマイグレーションファイルを編集します。

例えば、以下のようなタスクを実行するためのマイグレーションファイルを編集したいとします。

・tasksテーブルに、name、description、deadlineという3つのカラムを追加する。

・nameカラムとdeadlineカラムにはNOT NULL制約を設ける。

・deadlineカラムには既定値を設定する。

db/migrate/ディレクトリに作成されたファイルに以下のようなコードを追加します。

class AddColumnsToTasks < ActiveRecord::Migration[6.1]
  def change
    add_column :tasks, :name, :string, null: false
    add_column :tasks, :description, :string
    add_column :tasks, :deadline, :datetime, null: false, default: '2023-12-31'
  end
end


このように、add_columnメソッドを使用して、カラムを追加し、null: falseでNOT NULL制約を設定し、defaultで既定値を設定することができます。

3-4. マイグレーションの実行

マイグレーションファイルを作成した後、以下のコマンドを使用してマイグレーションを実行します。

rails db:migrate

このコマンドを実行すると、データベースにテーブルが作成されます。

3-5. モデルの操作

モデルが作成され、データベースにテーブルが作成されたら、ActiveRecordを使用してデータベースにレコードを作成、読み込み、更新、削除することができます。

以下は、それぞれの操作の例です。

・インスタンスを作成し、データを保存する


model = ModelName.new(name: "Taro", value: 123)
model.save

・レコードの作成


ModelName.create(name: "Taro", value: 123)

createを使用すると、newとsaveを同時に行うことができます。

・レコードの読み込み


model = ModelName.find(id)

・レコードの更新


model = ModelName.find(id)
model.name = "new name"
model.save

・レコードの削除


model = ModelName.find(id)
model.destroy


以上が、ActiveRecordを使うための基本的な手順です。

4. 一対多のモデルを定義する


実際のアプリケーション開発では、複数のテーブルを関連付けて開発することが普通です。

Railsで複数のモデルを扱うには、それぞれのモデルを定義し、必要に応じて関連付けを定義する必要があります。

以下は、2つのモデルを一対多で扱う方法の例です。

例として、BookとAuthorという2つのモデルを扱う方法を説明します。

4-1. BookモデルとAuthorモデルを定義し、関連付けを行う

# Bookモデルの定義
class Book < ApplicationRecord
  belongs_to :author
end

# Authorモデルの定義
class Author < ApplicationRecord
  has_many :books
end

Bookモデルにはbelongs_toメソッドがあり、Authorモデルにはhas_manyメソッドがあります。

これらのメソッドによって、BookモデルとAuthorモデルの間に関連性を定義することができます。

具体的には、BookモデルがAuthorモデルに属し、Authorモデルが複数のBookモデルを持つことができるようになります。

このような関連性を定義することで、例えばある著者の書いた本を取得する、ある本を書いた著者を取得するといったことが簡単に行えるようになります

4-2. コントローラーで処理を定義する

class BooksController < ApplicationController
  def index
    @books = Book.includes(:author).all
  end

  def show
    @book = Book.includes(:author).find(params[:id])
  end

  def new
    @book = Book.new
  end

  def create
    @book = Book.new(book_params)

    if @book.save
      redirect_to @book
    else
      render 'new'
    end
  end

  private
    def book_params
      params.require(:book).permit(:title, :description, :author_id)
    end
end

Bookモデルにはbelongs_toメソッドがあり、Authorモデルにはhas_manyメソッドがあるため、Book作成フォームでAuthorの選択を行う必要があります。

そのため、book_paramsメソッドにauthor_idを許可するパラメータを追加しています。


また、includesメソッドを使って、関連するauthorモデルのデータを事前に取得しています。

これにより、アクションごとにデータベースへのクエリ回数を削減し、N+1問題を解決することができます。

4-3. ビューでデータを表示する

<%# index.html.erb %>

Books



 
   
     
     
     
   
 
 
    <% @books.each do |book| %>
     
       
       
       
     
    <% end %>
 
タイトル著者名説明
<%= book.title %><%= book.author.name %><%= book.description %>

この例では、タイトルと説明をBookのデータから、著者名をAuthorのデータから表示するようにしています。

5. 多対多のモデルを定義する

Railsにおいて、例えば商品テーブルと業者テーブルを多対多に関連付けるには、中間テーブルを作成する必要があります。

中間テーブルには、商品と業者のIDをそれぞれ外部キーとして持ちます。

以下の手順に従って、多対多の関連を設定できます。

5-1. モデルを作成し関連付けを行う

商品テーブルと業者テーブルのモデルを作成し、それぞれにhas_and_belongs_to_manyを設定します。

class Product < ApplicationRecord
  has_and_belongs_to_many :suppliers
end

class Supplier < ApplicationRecord
  has_and_belongs_to_many :products
end


5-2. 中間テーブルを作成する

中間テーブルを作成します。中間テーブルは商品と業者のIDを持ち、それぞれ外部キーとして商品テーブルと業者テーブルを参照します。

テーブル名は、アルファベット順で両テーブル名を繋いだものが一般的です。

rails g migration CreateJoinTableProductsSuppliers products suppliers

生成されたマイグレーションファイルの中身は以下のようになります。

class CreateJoinTableProductsSuppliers < ActiveRecord::Migration[6.1]
  def change
    create_join_table :products, :suppliers do |t|
      # t.index [:product_id, :supplier_id]
      # t.index [:supplier_id, :product_id]
    end
  end
end


5-3. マイグレーションを実行

マイグレーションを実行します。

rails db:migrate

これで商品テーブルと業者テーブルが多対多の関係で関連付けられました。

5-4. Railsコンソールを開いて、関連付けを試す

関連付けが正しく行われたかどうかを確認するには、Railsコンソールを開いて、関連付けを試してみることができます。

product = Product.create(name: "Product A")
supplier = Supplier.create(name: "Supplier A")
product.suppliers << supplier
product.suppliers
# => #<ActiveRecord::Associations::CollectionProxy [#<Supplier id: 1, name: "Supplier A", created_at: "2023-04-10 09:00:00", updated_at: "2023-04-10 09:00:00">]>

中間テーブルを使用する場合、多対多の関係があります。

これは、中間テーブルを介して2つの異なるテーブルを結びつけるための方法です。


N+1問題は、ORM(Object-Relational Mapping)を使用するときに発生するクエリの効率性の問題であり、通常は単一のクエリで取得できる情報を取得するために、N個のクエリが実行されることを意味します。

しかし、中間テーブルを使用する場合、N+1問題は発生しません。

中間テーブルを介して必要なすべての情報を1回のクエリで取得できるためです。


例えば、商品テーブル、業者テーブル、そして中間テーブルを持つ場合、商品と業者の関係は中間テーブルで定義されます。

商品と中間テーブル、業者と中間テーブルの関係は、各テーブルの外部キーを使用して定義されます。

したがって、必要な情報を取得するために、1つのクエリを実行できます。

6. 複数のモデルを使ったアプリを作ってみる


では実際に、複数のモデルを使った「仕入帳」アプリを作ってみます。

以下のような仕様にします。

6-1. 基本設計:システム概要

本システムは、仕入帳を管理するアプリケーションである。

データベースには、仕入帳テーブル、商品テーブル、業者テーブルの3つを使用する。

トップページでは、日付、商品名、単価、個数、業者名の一覧を表示する。

また、商品名、仕入日、業者名で検索できるフォームを設置する。

すべての画面ヘッダーには、トップページへのリンク、仕入入力画面ページへのリンク、商品一覧ページへのリンク、業者一覧ページへのリンクを表示する。

6-2. 基本設計:データベース設計

データベースには、仕入帳テーブル、商品テーブル、業者テーブルの3つを使用する。

 仕入帳テーブルには、以下の情報を保持する。

・仕入日
・個数
・対応する商品テーブル
・対応する業者テーブル

 商品テーブルには、以下の情報を保持する。

・商品名
・単価

 業者テーブルには、以下の情報を保持する。

・業者名
・業者電話番号

6-3. 画面設計

本システムは、以下の4つの画面から構成される。


・トップページ
・仕入入力画面ページ
・商品一覧ページ
・業者一覧ページ


 トップページでは、以下の要素を表示する。


・日付、商品名、単価、個数、業者名の一覧
・商品名、仕入日、業者名で検索できるフォーム
・仕入詳細ページへのリンクボタン


 仕入入力画面ページでは、以下の要素を表示する。


・日付、商品名、個数、業者名の入力フォーム
・登録ボタン


 仕入詳細ページでは、以下の要素を表示する。


・日付、商品名、個数、業者名の表示
・仕入編集ページへのリンクボタン
・仕入削除ボタン


 仕入編集ページでは、以下の要素を表示する


・日付、商品名、個数、業者名の編集フォーム
・更新ボタン


 商品一覧ページでは、以下の要素を表示する。


・商品名、単価、在庫数の一覧
・商品名で検索できるフォーム
・商品詳細ページへのリンクボタン


 商品詳細ページでは、以下の要素を表示する。


・商品名、単価、在庫数の表示
・商品編集ページへのリンクボタン
・商品削除ボタン


 商品編集ページでは、以下の要素を表示する。


・商品名、単価、在庫数の編集フォーム
・更新ボタン


 業者一覧ページでは、以下の要素を表示する。


・業者名、業者電話番号の一覧
・業者名で検索できるフォーム
・業者詳細ページへのリンクボタン


 業者詳細ページでは、以下の要素を表示する。


・業者名、業者電話番号の表示
・業者編集ページへのリンクボタン
・業者削除ボタン


 業者編集ページでは、以下の要素を表示する。


・業者名、業者電話番号の編集フォーム
・更新ボタン


詳細設計以降は、後日また改めて学習します。

7. まとめ

RailsはORM(Object-Relational Mapping)を採用しており、テーブルの操作をオブジェクト指向的な方法で行うことができます。

Railsでは、ActiveRecordというORMが提供されており、SQL文を直接書く必要がありません。

また、ActiveRecordは様々な便利なメソッドを提供しており、複雑なSQL文を書く必要がなくなる場合もあります。


ただし、複雑なクエリを発行する場合や、パフォーマンスが重要な場合は、SQLを直接書いた方が良い場合があります。

RailsでもSQLを直接書くことは可能であり、ActiveRecordによるデータ操作とSQLの併用が一般的に行われています。


総合的に考えると、テーブルの数が増えても、ActiveRecordでデータ操作ができる場合は、SQLを直接書く必要はなく、ActiveRecordを使用することが望ましいと言えます。

ただし、パフォーマンスの観点から、場合によってはSQLを直接書く必要があることもあります。

今後RailsでSQLを直接書く方法も、学んでいきたいと思います。


本日は以上となります。

ありがとうございました。