🍺

Ruby on Railsでデータベース(MySQL)のテーブル作成をしてみた話

2022/03/05に公開

きっかけ

本当はORMの話を書くつもりでしたが、テーブル作成までで一旦区切ることにしました。

手順

事前準備

Docker

今回はMySQLを使います。

Rubyに関しては
https://zenn.dev/nobokko/articles/tech_ruby_rails_firststep
と同様のDockerfileを使用しています。

mysql/Dockerfile
FROM mysql:latest

RUN echo '\
[mysqld]\n\
character-set-server=utf8mb4\n\
collation-server=utf8mb4_unicode_ci\n\
\n\
[client]\n\
default-character-set=utf8mb4\n\
' > /etc/mysql/conf.d/my.cnf
docker-compose.yml
version: '3'
services:
  db:
    build: ./mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      TZ: "Asia/Tokyo"
    ports:
      - "3306:3306"
  web:
    image: ruby
    build: .
    ports:
      - "3000:3000"
    volumes:
      - /d/github/xxx:/home/rubyuser/git-workspace/xxx
    depends_on:
      - db
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
      - PMA_USER=root
      - PMA_PASSWORD=root
    depends_on:
      - db
    ports:
      - "3001:80"

Railsプロジェクトの作成

コンテナ内の展開先で以下のコマンドを叩いていきます。

今回はデータベースにMySQLを使用するのでオプションで「使用するSQL」を指定しています。

rails _6.1.4.1_ new . --force --minimal --skip-test --database=mysql

SQL設定

SQLに関する設定が無いとSQLに繋がらないので、まずはその設定を加えます。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: root
  host: db

ポイント

  • 【password】はdocker-compose.ymlで指定しているMYSQL_ROOT_PASSWORDです。
  • 【host】はdocker-compose.ymlで指定しているservicesのMySQLが動いているコンテナ名*dbです。

上記設定後、rakeのコマンドでこのRailsプロジェクトが使用するデータベースを作成します。

rake db:create

同ディレクトリで以下のコマンドを実行してブラウザで http://localhost:3000/ にアクセスするととりあえず動作していることが確認できました。

rails s -p 3000 -b '0.0.0.0'

テーブルの作成

単純なテーブル

一般ユーザー[1]を想定したテーブルを定義します。

rails generate scaffoldでrails側にファイルを作成します。

このコマンドでテーブルの情報から、そのテーブルに関するCRUDが一通りできるMVC各ファイル他が作成されたり更新されたりします。

rails generate scaffold Normal_User name:string mail:string last_login_at:datetime

id、created_at、updated_atは明示的に指定していませんが暗黙的に作成されます。

rails generate scaffoldの実行だけではMySQLサーバー側には反映されていないので、それを反映します。

rake db:migrate

ブラウザで http://localhost:3000/normal_users にアクセスすると色々操作できます。

外部キーを持つテーブル

一般ユーザーが日常を投稿することが可能なテーブルを想定します。
このテーブルのレコードは必ず一般ユーザーが登録されていることを前提とします。

テーブル名は「normal_users」ですが、rails g を実行する際は「normal_users」を指定すると整合性が取れないのか、saveでエラーしました。sを付けずに「normal_user」とする必要があるようです。

rails generate scaffold Everyday_Post normal_user:references everydaypost_comment:string

MySQLサーバー側に反映します。

rake db:migrate

NOT NULL 制約 & DEFAULT & UNIQUE 制約

既に一部で勝手にnot null制約が付与されていますが、明示的にnot null制約を持つカラムを含むテーブルを追加してみます。
ついでにデフォルト値設定を加えます。
作成しようとしたテーブルがユニーク制約が合った方が良さそうな気がしたのでユニーク制約も付けます。

残念ながら現時点ではnullやデフォルトをrails generateコマンドで設定するのは難しいようなので、一旦これらは無かったことにしてファイルを作成します。

rails generate scaffold User_Setting normal_user:references:uniq use_notification:boolean

こんなファイルが作成されました。数字の部分は実行時に依存すると思うのでdb/migrate/配下からそれっぽいファイル名のものを探してください。

db/migrate/20220304172707_create_user_settings.rb
class CreateUserSettings < ActiveRecord::Migration[6.1]
  def change
    create_table :user_settings do |t|
      t.references :normal_user, index: {:unique=>true}, null: false, foreign_key: true
      t.boolean :use_notification

      t.timestamps
    end
  end
end

NOT NULL制約とDEFAULTを追加します。

db/migrate/20220304172707_create_user_settings.rb
class CreateUserSettings < ActiveRecord::Migration[6.1]
  def change
    create_table :user_settings do |t|
      t.references :normal_user, index: {:unique=>true}, null: false, foreign_key: true
      t.boolean :use_notification, null: false, default: 'true'

      t.timestamps
    end
  end
end

MySQLサーバー側に反映します。

rake db:migrate

「ID」以外をPRIMARY KEYとする

先に追加したテーブルはユニーク制約を追加してみたかったので一旦そうしたのですが、このテーブルであればnormal_user自体がプライマリである方が自然というか間違いが起きづらい気がします。

ここでは既存テーブルの変更ではなく、無かったものとして話を進めます。

rails generate scaffold User_Setting normal_user:references use_notification:boolean

IDの作成を抑止し、プライマリーキーを別途明示します。

db/migrate/20220304172707_create_user_settings.rb
class CreateUserSettings < ActiveRecord::Migration[6.1]
  def change
    create_table :user_settings, id: false do |t|
      t.references :normal_user, null: false, foreign_key: true, primary_key: true
      t.boolean :use_notification, null: false, default: 'true'

      t.timestamps
    end
  end
end

MySQLサーバー側に反映します。

rake db:migrate

このままだとIDが無いのでrails generate scaffoldで生成された画面が動きません。

色々修正します。

まずルーター。

「param:」を付けて明示しておきます。
これ自体はそのままIDで使っても動くかもしれませんが後々混乱を招きかねません。

config/routes.rb
Rails.application.routes.draw do
  # resources :user_settings
  resources :user_settings, param: :normal_user_id
  resources :everyday_posts
  resources :normal_users
end

idでfindしているので、normal_user_idに変更します。

app/controllers/user_settings_controller.rb
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user_setting
      # @user_setting = UserSetting.find(params[:id])
      @user_setting = UserSetting.find(params[:normal_user_id])
    end

http://localhost:3000/user_settings から登録済みのnormal_userのIDしか登録できないことが確認できると思います。

phpMyAdmin

名前に「php」と入っていますが、php用ではなくPHPで実装されたMySQLサーバ向けのデータベース接続クライアントツールです。

http://localhost:3001/

railsの確認が出来ているのであれば上記アドレスでアクセスできると思います。

DBの値のチェックはrailsの画面ではなく、データベース接続クライアントツールを使って確認してください。

必ずしもphpMyAdminでなければいけないわけではありません。データベース接続クライアントツールは色々あるので自分に合うものを探すと良いと思います。(というよりセキュリティリスクにしかならないのでネットワーク上の環境にphpMyAdminを配置するのは避けた方が良いです)

今回のコマンドを写経すると画像のような感じになります。

まとめ

rails generate scaffold テーブル名 カラム名1 カラム名2 ...

を実行して

rake db:migrate

でMySQLサーバーへ反映する。

本文中では触れていませんが、railsのCRUDが生成時に不要であればscaffoldの代わりにmodel等を指定するのもよいかもしれません。

お疲れさまでした。

脚注
  1. 個人的に1単語のテーブル名はあまり好きではないので私が作成するテーブルは大体二単語以上です。カラム名も同様なのですが参考にした元のまま端折っちゃいました。 ↩︎

Discussion