概要
みなさんこんにちはcandleです。今回はrakeコマンドのややこしいところを扱います。
rakeコマンドはわりと便利で、データベースのカラムの追加や型の変更をマイグレーションファイルから読み込む事で、
複数人でウェブアプリケーションを開発してもデータベースに齟齬が生じない様になっているものです。
一方でrakeコマンドを使うからこそ、柔軟なデータベースの変更が出来ずに苦労することはrailsを使っている人なら一度はあるでしょう。
今回もそのrakeコマンドを使って、カラムの構造を変更します。
前提
railsの実行環境が整っている。
テスト用のモデルを作る
それではテスト用のカラムを作り、そこでrakeコマンドのテストをします。
railsプロジェクトの中で、下のコマンドを実行して、モデルを作成します。
私は例によって、bootstrap3というrailsプロジェクトを使います。
rails g model dbtest name:string description:text
下のコマンドでマイグレーションファイルをデータベースに適応させます。
bundle exec rake db:migrate
これでdbtestモデルができました。
データベースを確認する
コマンドラインから、データベースにアクセスします。
rails db
まずはテーブルができているか確認します。
mysqlを使っているなら
show tables;
sqliteなら
.table
です。
ありました。次にカラムの型を確認します。
mysqlなら
DESC dbtests;
sqliteなら
.schem dbtests
です。
テーブルにレコードを追加する
試しに、railsのコマンドラインからデータを挿入してみましょう。
下のコマンドをrailsプロジェクトの中で実行しましょう。
rails console
aliceさんのデータを作ります。
user1 = Dbtest.new({name: 'alice', description: 'hello'}) user1.save
tomさんのデータを作ります。
tomさんはあえてdescriptionを指定しません。つまりデータベースにnullを入れます。
user2 = Dbtest.new({name: 'tom'}) user2.save
データがデータベースに保存されたか確認します。
データベースにログインして
rails db
下のコマンドを実行します。
SELECT * FROM dbtests;
いいですね。
これで準備ができました。
カラムの型とデフォルト値を変更する
先ほど、dbtestsテーブルのdescriptionカラムの型は大きいtext型にしましたが、
よくよく考えたら、vachar255のstring型で良い事に気づいたとします。
また、default値を空文字列にします。
descriptionカラムの型を変更しましょう。
下のコマンドでカラム変更の為のmigrationファイルを作成します。
rails g migration ChangeDescriptionOfDbtests
作成したマイグレーションファイルをエディタで開きます。
始めにdef change 関数を削除します。
というのも、def change関数はrake コマンドのrollbackに対応していないからです。
rakeコマンドのrollbackとはデータベースの状態をマイグレーションファイルをもとに過去のデータベース構造に戻すコマンドです。
ところが、def changeで実行されたchange_columnはデータベースの構造を変更する事はできるのですが、それを戻す事はできません。
正直、私はそんなバカな事があるのかと思いましたが、考えてみれば、確かに何を参照して、前のデータベース構造に戻したら良いのかわからないですよね。
ただ、実際テーブルを変更するとなると、また何時戻すとも限らないのでself.up関数とself.down関数を使ってテーブルの型の変更とデフォルト値の設定をします。
def self.up change_column :dbtests, :description, :string, :null => false, :default => "" end def self.down change_column :dbtests, :description, :text,:limit => nil ,:null => true,:default => nil end
self.up関数では新しいカラム構造を記述します。ここではdescriptionカラムをstring型にして、デフォルト値を空文字列にしています。
self.downでは前のデータベースの構造を記述します。こうすることでrollbackに対応させます。
(余談ですが、mysqlおよびsqliteのデフォルト値Nullによるrailsのnil判定エラー、例えば.empty?や.present?などを回避したい場合はdefault値で何も無いという文字列 :default => “”を設定すると良いでしょう。)
保存しましょう。
下のコマンドで、データベースに反映させます。
bundle exec rake db:migrate
これで、変更できたと思います。
確認してみましょう。データベースにログインしてスキーマを確認すると
DESC dbtests;
または
.schem dbtests
まとめ
rakeコマンドのテーブルの変更はなかなか難しいところがありますが、これでやっていけるのではないでしょうか?質問などがあればコメント欄からお願いします。