RailsでMySQLのviewテーブルをmigrationで管理する


概要

Railsでは標準でviewテーブルをmigrationで管理することができない。
そこで、scenicというgemを使って実現する。

ただし、このgemは標準でPostgreSQLにしか対応していないので、scenic-mysql_adapterというgemも使ってMySQLで使えるようにする。

Installation

gemをインストール

以下を追記

Gemfile
gem 'scenic', '~> 1.5', '>= 1.5.4'
gem 'scenic-mysql_adapter', '~> 1.0', '>= 1.0.1'

Install

$ bundle install --path vendor/bundle/

scenic.rbの作成

config/initializers/scenic.rbを作成する

config/initializers/scenic.rb
require 'scenic/mysql_adapter'

Scenic.configure do |config|
  config.database = Scenic::Adapters::MySQL.new
end

Usage

migrationファイルを生成

以下はsearch_resultsというviewテーブルを作成する例。

$ bundle exec rails generate scenic:view search_results
      create  db/views/search_results_v01.sql
      create  db/migrate/[TIMESTAMP]_create_search_results.rb

生成された以下のファイルに、viewテーブルを作成するDDLのcreate文以外の箇所を記載する。

db/views/search_results_v01.sql
SELECT
  statuses.id AS searchable_id,
  'Status' AS searchable_type,
  comments.body AS term
FROM statuses
JOIN comments ON statuses.id = comments.status_id

UNION

SELECT
  statuses.id AS searchable_id,
  'Status' AS searchable_type,
  statuses.body AS term
FROM statuses

migrate

$ bundle exec rake db:migrate

テスト用テーブルの作成も忘れずに

$ bundle exec rake db:test:prepare

Updateする場合

以下を実行するとsearch_results_v02.sqlが作成されるので、新しいDDLを記載し、rake db:migrateする。

$ bundle exec rails generate scenic:view search_results
      create  db/views/search_results_v02.sql
      create  db/migrate/[TIMESTAMP]_update_search_results_to_version_2.rb

db/schema.rbも変更されるので確認すること。

ちなみに

scenic-mysql_adapterを導入せずにmigrateすると、テーブルは作成されるが以下のエラーが出る。

$ bundle exec rake db:migrate
rake aborted!
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'current_schemas(false))
            ORDER BY c.oid' at line 11:             SELECT
              c.relname as viewname,
              pg_get_viewdef(c.oid) AS definition,
              c.relkind AS kind,
              n.nspname AS namespace
            FROM pg_class c
              LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
            WHERE
              c.relkind IN ('m', 'v')
              AND c.relname NOT IN (SELECT extname FROM pg_extension)
              AND n.nspname = ANY (current_schemas(false))
            ORDER BY c.oid

リンク