rails初心者がデータベース移行試みたのでまとめてみた。

rake taskを使ったデータベース移行を行ったので記録として書いておこうと思います。ruby初心者なので後々振り返られるようになるべくやったこととエラーを時系列で書いていこうと思います。

#やりたいこと
現状では同じページ内で扱っているデータが違うテーブル内にあることからviewが乱雑になりがちになっていました。そこで今回はDBのテーブルをきれいに整理すると同時に新たにpriorityというカラムを作ってそのpriorityベースでデータを配列内で管理するための下準備を行いたいと思っています。ざっくり言えばrake taskを使ってのデータベースの移行。移行にあたってテーブル名やレコードの値の変更などです。

#やらないといけないこと
具体的には:
- <a href="http://qiita.com/weedslayer/private/99dfb5e30f978afde436#データベースにpriorityコラムの追加">データベースにpriorityコラムの追加</a>
- <a href="http://qiita.com/weedslayer/private/99dfb5e30f978afde436#モデルの書き換えとpriorityの反映、validationの確認">モデルの書き換えとpriorityの反映、validationの確認</a>
- <a href="http://qiita.com/weedslayer/private/99dfb5e30f978afde436#rake_taskの作成">rake_taskの作成</a>
- <a href="http://qiita.com/weedslayer/private/99dfb5e30f978afde436#controllerの編集priorityに1がついているものを優先的にもってくる">controllerの編集priorityに1がついているものを優先的にもってくる</a>
- viewの編集

くらいだと思います。DBからviewにそって作業を行っていけば途中でエラーが起きても対処しやすく確認作業も行いやすいのではと思ったことがこの順番にした主な理由です。


[Railsでカラムのデータ型を変更する場合の手順](http://blog.jnito.com/entry/20120514/1336951768)が今回の作業を進めていく上で非常に参考になりました。

#データベースにpriorityコラムの追加

参考にしたリンク:
- [新しいマイグレーションを追加してテーブルを変更](http://www.rubylife.jp/rails/model/index7.html)
- [ruby on railsのデータベースのテーブルにカラムを追加、削除する](https://joppot.info/2014/05/23/1430)

`rails g migration AddPriorityToCategory_Contents priority:integer`

![Screen Shot 2016-10-31 at 11.05.26 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/3dd2012d-faff-57ec-e903-fec19aacb49e.png)

db/migrate内に新たに出来たファイルに

```
class AddPriorityToCategoryContents < ActiveRecord::Migration
  def change
    add_column :category_contents, :priority, :integer
  end
end
```

がちゃんと書き込まれていることを確認した後、`rake db:migrate`を実行。ちなみに`mysql.server start`コマンドでサーバーをオンにしていないとエラーが起きます。

![Screen Shot 2016-10-31 at 11.08.37 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/2783184f-df22-85ef-e348-d0716e3ca963.png)

実際にmysqlにログインして確認してみる。

![Screen Shot 2016-10-31 at 11.09.54 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/07302953-b574-d15d-0439-fe4e21520428.png)

#モデルの書き換えとpriorityの反映、validationの確認

[このへんを](http://edgeguides.rubyonrails.org/active_record_validations.html)参考にしながら

```
class CategoryContent < ActiveRecord::Base
  belongs_to :category

  validates :category_id, presence: true
  validates :title, presence: true
  validates :content, presence:true
  validates :is_public, presence: true, inclusion: {in: [true, false] }
  validates :priority, presence: true, numericality: {only_integer: true}
end
```
に書き換える。

#rake_taskの作成

この辺を読みながら作ってみました。
- [Rake タスクの作り方（引数を複数設定してみる）](http://qiita.com/yoshiokaCB/items/c97ba878469701c3d99b)
- [Rakeタスクをつくる](http://qiita.com/mosson/items/9a9b0703ac4c76ebfd4e)
migration task関係の記事を見つけたので以下も参考になるかと思います。
- [Rails で信頼性の高い Migration を書くには
](http://qiita.com/shuhei/items/c0a6c3e29c87de6dff63)

やりたいことは:

- category_path_contentsからcategory_contentsへの移行。
- `description`の個数は必ず1か`description`自体がそもそも存在しない(=nil)
- `text`の個数は複数もしくはそもそも存在しない
- 移行の際、テキストデータが`.description`のものであれば`priority`を1に。そうではない`.text`のものであれば`priority`を0に

##DBについて考えたこと
`text`と`desc`をそれぞれ統合して一つの要素を作るというよりは`text`という要素と`desc`を持った要素が数に応じて存在しておりそれぞれ`category_id`によって紐付けされているという感覚です。理由はmysqlデータベースの形として出来るだけ行（カラムの数）を増やすのではなく列を増やすことによってデータベースへの負担や長期的に起きうる不安要素を事前に排除できるからです。具体的には:

`{category_id:1, description:hoge, text1:hoge, text2:hoge}`だと第一に`text`が何個入ってくるかわからないためそれに対応して新たにコラムを作ることになってしまいます。またレコードによってはそのコラムが必要無い場合もあるわけです。つまり

| category_id | description | text1 | text2 |
|:-:|:-:|:-:|:-:|
|  1 |  hoge |  hogehoge | hogehogehoge  |
| 1  | nil  | hogehoge  | nil  |
| 2  | hoge  |  nil | nil  |
| 3  | hoge  | hoge  |  hoge |

あまりきれいな形ではありませんね。そこで

| category_id | description |
|:-:|:-:|
| 1  | hoge  |
| 2  |  hog |
| 3  | hoge  |

という`description`を管理するテーブルと

| category_id | text |
|:-:|:-:|
| 1  |  hoge |
| 1  | hoge  |
|  1 |  hoge |
|  2 |  hoge |

という`text`を管理するテーブルを別々に用意してあげれば、数に応じて新しくレコードを増やすだけなので負担が少なく済みます。

詳しい解説は<a href="https://www.amazon.co.jp/%E9%81%94%E4%BA%BA%E3%81%AB%E5%AD%A6%E3%81%B6DB%E8%A8%AD%E8%A8%88-%E5%BE%B9%E5%BA%95%E6%8C%87%E5%8D%97%E6%9B%B8-%E5%88%9D%E7%B4%9A%E8%80%85%E3%81%A7%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%9F%E3%81%8F%E3%81%AA%E3%81%84%E3%81%82%E3%81%AA%E3%81%9F%E3%81%B8-%E3%83%9F%E3%83%83%E3%82%AF/dp/4798124702/ref=sr_1_1?ie=UTF8&qid=1477894976&sr=8-1&keywords=%E9%81%94%E4%BA%BA%E3%81%AB%E5%AD%A6%E3%81%B6db%E8%A8%AD%E8%A8%88+%E5%BE%B9%E5%BA%95%E6%8C%87%E5%8D%97%E6%9B%B8" target="_blank">達人に学ぶDB設計徹底指南書</a>を参考にすると良いかもしれません。

##スクリプト

```
    desc "migrate category to new db"
    task migrate_category: :environment do
      GakkiManiaCategoryPathContent.find_each do |gmc|
        if gmc.text != nil
          gmc.public_state = true
        else
          gmc.public_state = false
        end
        CategoryContent.create(
          category_id: gmc.id,
          title: gmc.headline,
          content: gmc.text,
          is_public: gmc.public_state
        )
      end
      GakkiManiaCategoryPathSetting.find_each do |gms|
        if gms.text != nil
          gms.public_state = true
        else
          gms.public_state = false
        end
        CategoryContent.create(
          category_id: gms.id,
          title: gms.name,
          content: gms.description,
          is_public: gms.public_state
        )
      end
    end
  end
    end
  end
```

`bundle exec rspec`でテストしてみる。


#エラーと対処
問題: rake_taskプログラムは動いているのにDBに反映されていない。
原因: modelでデータを判別をしている際に`validation`周辺に引っかかっているのではないか。

```ruby:category_content.rb
validates :category_id, presence: true
  validates :title, presence: true
  validates :content, presence:true
  validates :is_public, presence: true, inclusion: {in: [true, false] }
```

##実際に行ってみたこと:
実際に`puts gmc.is_public`でコンソールに流してみると全て`0`だった。色々辿っていくと旧DBではレコードがINT扱いに対して新DBではブーリアンに変わっていることに気づく。そこで`enum public_state: {draft: 0, closed: 10, published: 20}`を参考に`enum`辺りが理解できれば直るかもしれないとのこと。(enumについては<a href="http://ruby-rails.hatenadiary.com/entry/20150710/1436461745" target="_blank">いまさらながらRails4.1から導入されたEnumが便利なのでまとめてみた</a>を参考にしました。)

データベースのレコードへのアクセスの仕方に関しては、

- <a href="https://openbook4.me/projects/92/sections/498" target="_blank">データベースのデータを表示する。</a>
- <a href="https://hackhands.com/ruby-rails-tutorial-creating-rails-instance-existing-mysql-db/" target="_blank">Ruby on Rails Tutorial: Creating a Rails Instance from an Existing MySQL DB</a>
- <a href="http://hmu29.hatenablog.com/entry/2014/02/14/001513" target="_blank">rails 既存DBを使ってrailsしてみた。</a>の「モデルのテーブル名を変更」について書かれているあたり。
- <a href="http://qiita.com/mr-myself/items/cfb936dcf63d2c44d2f5" target="_blank">【Rails初心者必見！】ひたすら丁寧にデータ取得を説明(find, where)</a>

が参考になると思います。

原因はブーリアンを持ってくるはずの`is_public`になぜか数字が送られてきていたためでしたが。。