rails/良く行う作業手順

(324d) 更新


公開メモ

概要

rails は便利なのですが、 たまにしか触らない「なんちゃって Web デベロッパー」としては、 そのつど rails の規約を思い出すのが苦痛なのです。

そこで、思い出せなかった手順をここにメモろうという魂胆です。

環境は Debian 上の apache2 上の fastcgi です。

データベースにカラムを追加する

まずは現状を確認します。

sqlite3 の使い方はこちらを参照しました。 http://tterry.blog.so-net.ne.jp/2008-03-19-1

LANG:console
$ sqlite3 db/production.sqlite3
 sqlite3> .schema some_entries
  CREATE TABLE "some_entries" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
  "column1" integer, "column2" boolean, "column3" integer, "column4" boolean, 
  "column5" date, "column6" date, "column7" integer, 
  "column8" text, "column9" text, "column10" text, "column11" boolean, 
  "changed_at" datetime, "created_at" datetime,
  "deleted" boolean DEFAULT 'f' NOT NULL);
 sqlite> .quit
$ 

これまでのカラムの命名法を参考に、 フィールド名を column12 に決めたとします。

次にマイグレーション用のひな型を作って、それを編集します。

rake migrate の使い方はこちらを参照しました。 http://tech.feedforce.jp/railsmigration.html

LANG:console
$ rails generate migration add_column12_to_some_entries
     exists  db/migrate
     create  db/migrate/20101018065146_add_column12_to_some_entries.rb
$ jed db/migrate/20101018065146_add_column12_to_some_entries.rb
 class AddColumn12ToSomeEntries < ActiveRecord::Migration
   def self.up
       add_column :some_entries, :column12, :text
   end
 
   def self.down
       remove_column :some_entries, :column12, :text
   end
 end
$

そしてマイグレーション。

LANG:console
$ rake db:migrate
 (in /home/samba/www/event/rails/ICSPM18)
 ==  AddRegistrationsPayaccount: migrating =====================================
 -- add_column(:registrations, :payaccount, :text)
    -> 0.0546s
 ==  AddRegistrationsPayaccount: migrated (0.0549s) ============================
$

これで development 環境が更新されます。

production 環境へも追加するなら、次を行うわけですが・・・

LANG:console
$ rails db:migrate RAILS_ENV=production
 (in /home/samba/www/event/rails/ICSPM18)
 ==  AddRegistrationsPayaccount: migrating =====================================
 -- add_column(:registrations, :payaccount, :text)
    -> 0.0831s
 ==  AddRegistrationsPayaccount: migrated (0.0837s) ============================
$

これに気づかず1時間くらい考え込んだりするわけです(TT

データベースからカラムを削除する

普通に考えると、

LANGUAGE:ruby
class RemoveSomeColumnFromTheTable < ActiveRecord::Migration[5.1]
  def up
    remove_column :the_table, :some_column
  end
  def down
    add_column :the_table, :some_column, :column_type
  end
end

で良さそうに思えるのですが、古い ActiveRecord ではデータベースが sqlite3 だと

LANGUAGE:console
$ rails db:migrate
 ...

 -- remove_column(:the_table, :some_column)
 rails aborted!
 StandardError: An error has occurred, this and all later migrations canceled:
 
 SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "the_table"

などと言って migration が失敗してしまいます。

どうやら sqlite3 では remove_column がサポートされていないらしく、 内部では一旦 DROP TABLE してから新たに CREATE TABLE して、 データのコピーやらなんやらしているらしいのですが、 その際に外部キー制約がかかっていると制約違反でエラーになってしまうようで、、、

この問題は ActiveRecord の 6.0 以上では解決しているのだけれど、 5.x 系列ではうまく行かないのだそうです。

どうしたらいいかというと 6.0 のコードを参考にして

LANGUAGE:ruby
class RemoveSomeColumnFromTheTable < ActiveRecord::Migration[5.1]

  # sqlite は drop_column がないので一旦 drop table して
  # 作り直すことになるのだけれど、そのままやると foreign_key
  # 制約に引っかかるので PRAGMA foreign_keys でくくってやる
  # 必要がある。これをサポートしていない古い ActiveRecord の
  # ために、最新版のコードからもらってきて以下のようにした。
  # https://my.diffend.io/gems/activerecord/4.2.11.3/6.0.0/page/34
  def ignore_foreign_keys
    if ActiveRecord::Base.connection_config[:adapter] != 'sqlite3'
      yield     # sqlite3 じゃなければ普通に実効
    else        # sqlite3 の場合は foreign key 制約を無効にしてから実効
       old_foreign_keys = query_value("PRAGMA foreign_keys")
        old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
        begin
          execute("PRAGMA defer_foreign_keys = ON")
          execute("PRAGMA foreign_keys = OFF")
          yield
        ensure
         execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
          execute("PRAGMA foreign_keys = #{old_foreign_keys}")
        end
    end
  end

  def up
    ignore_foreign_keys do
      remove_column :the_table, :some_column
    end
  end
  def down
    add_column :the_table, :some_column, :column_type
  end
end

のように remove_column の前後で PRAGMA を変更して、 作業中に foreign_keys 制約を無視させれば良いようでした。

エラーが出た時に参照すべきログファイル

apache2 のログファイルが /var/log/apache2/* にあるので、 まずはこれを参照します。

rails 固有のメッセージはプロジェクトフォルダの下の
(rails)/log/*.log
にあるので、これを見ればよいようです。

コメント





Counter: 5218 (from 2010/06/03), today: 1, yesterday: 0