rakeでmigration
最近の rails はテーブルの作成は、rake の migration というのを使うらしい。
http://www.atmarkit.co.jp/im/carc/serial/proto04/proto04.html
(↑この記事は細かいところが結構間違っている。。)
RakeはRuby Make、つまり、Ruby版のビルドツールで、それを使って ActiveRecord::Migration を利用する。
使い方
ひな形を作って、それを編集する。
以下の例では、membersテーブルを作成するテンプレートを生成。
$ ./script/generate migration create_members
これを実行すると、以下のような db/migrate/001_create_members.rb ができる。
class CreateMembers < ActiveRecord::Migration def self.up end def self.down end end
このひな型はバージョン管理されていて、ひな形を作るごとに、頭の001が002、003、、と増えていく。self.up には以前のバージョンからの追加分を定義し、self.down には以前のバージョンに戻すときの変更分を定義する。今回は初回なので、まずは create の定義だけでOK。(正確に言うとdownにはdropを定義するんだろうか。。。?)
以下、テーブル作成定義を追加したもの。
class CreateMembers < ActiveRecord::Migration def self.up create_table :members do |t| t.column :id, :integer t.column :name, :string end end def self.down end end
編集したら、rakeを使って変更を反映。
$ rake db:migrate
変更を確認。(SQLiteの場合)
$ sqlite3 db/development.sqlite3 sqlite> .schema CREATE TABLE members ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255) DEFAULT NULL); CREATE TABLE schema_info (version integer); sqlite> .quit
membersテーブルが作成されていることがわかる。(注:上記例は見やすいように改行してある。)
rails 2.0でscaffold
railsのバージョンを2.0にして、sqliteを使うようにしたので、初めから scaffoldで作ってみることにした。が、色々と変更されていて非常にはまった。。。
参考:http://d.hatena.ne.jp/idesaku/20071211/1197386955
これまでだと、データベースにテーブルを作って(migrateから作ったりも含む)、その内容をもとにmodelを生成し、scaffoldを作っていた。しかし、2.0からはいきなり scaffold から作成するように変更された。scaffoldを作成するときにオプションでモデルのフィールドを指定する。従来通りの方法でやろうとすると、scaffoldが作成できなくて泣くはめになる(自分)
以下 2.0での構築方法。
まず、いきなりscaffoldを作成。nameとageという属性をもったMemberモデルを使用。
$ ruby script/generate scaffold Member name:string age:integer ・・・
オプションで与えた属性をもとに、db/migrate/001_create_members.rb が作成される。
中身はこうなっている。
class CreateMembers < ActiveRecord::Migration def self.up create_table :members do |t| t.string :name t.integer :age t.timestamps end end def self.down drop_table :members end end
id属性は特に明示しなくても、デフォルトでつけてくれるようだ。
これをデータベースに反映
$ rake db:migrate
以上で終了。以前に比べてテーブルの作成やモデルの生成手順が減ってすっきりした感がある。新しい方法でやらないと結構はまるので要注意。
Rails 2.0.2でサンプルアプリ
ちょっとましなサンプルを2.0.2の作法で作ってみることにする。
参考:http://www.kestrel.jp/modules/tinyd04//content/index.php?id=1
上記URLを参考に、rails2.0流に。
1.ベースの作成
適当なところで、いつものベースの作成
$ rails rails_2.0-sample create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create config/initializers ・・・ $ cd rails_2.0-sample
2.足場(scaffold)の作成
今回、Person と Person の属する Organazation の二つのモデルを使います。rganazation は名前(name)を持ち、Person は名前(name)と所属するOrganazationのid(orgId)をもちます。それぞれ scaffoldを作成します。
$ ruby script/generate scaffold Organization name:string exists app/models/ exists app/controllers/ exists app/helpers/ create app/views/organizations exists app/views/layouts/ ・・・ $ ruby script/generate scaffold Person name:string orgId:integer exists app/models/ exists app/controllers/ exists app/helpers/ create app/views/people exists app/views/layouts/ ・・・
scaffold作成の引数に、それぞれ持っている属性を指定します。なお、Person は人の単数形で、テーブル名は自動的に複数形のPeopleに変換されています。
migrateファイルもそれに合わせて自動的に生成されるので、rakeでデータベースへ反映します。
$ rake db:migrate (in /xxx/xxx/rails_2.0-sample) == 1 CreateOrganizations: migrating =========================================== -- create_table(:organizations) -> 0.0038s == 1 CreateOrganizations: migrated (0.0039s) ================================== == 2 CreatePeople: migrating ================================================== -- create_table(:people) -> 0.0037s == 2 CreatePeople: migrated (0.0039s) =========================================
3.データベースの確認
一応、sqliteで確認。
$ sqlite3 db/development.sqlite3 SQLite version 3.5.2 Enter ".help" for instructions sqlite> .schema CREATE TABLE organizations ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255) DEFAULT NULL, "created_at" datetime DEFAULT NULL, "updated_at" datetime DEFAULT NULL); CREATE TABLE people ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255) DEFAULT NULL, "orgId" integer DEFAULT NULL, "created_at" datetime DEFAULT NULL, "updated_at" datetime DEFAULT NULL); CREATE TABLE schema_info (version integer); sqlite>.q
4.サーバ起動と確認
以上で基本的な設定は終了。サーバーを起動して動作確認。
$ ./script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options
http://HOSTNAME:3000/organization、http://HOSTNAME:3000/people にアクセスするとそれぞれ、組織、人のリストが見れます。
4.表示カラムの追加
peopleの一覧画面をみると、Name と Organization だけ表示されています。ここに、IDと作成日時、更新日時も表示するように修正してみる。app/views/people/index.html.erb を編集する。
<h1>Listing people</h1> <table> <tr> <th>id</th> <!-- ← 追加 --> <th>Name</th> <th>Orgid</th> <th>作成日時</th> <!-- ← 追加 --> <th>更新日時</th> <!-- ← 追加 --> </tr> <% for person in @people %> <tr> <td><%=h person.id %></td> <!-- ← 追加 --> <td><%=h person.name %></td> <td><%=h person.orgId %></td> <td><%=h person.created_at %></td> <!-- ← 追加 --> <td><%=h person.updated_at %></td> <!-- ← 追加 --> <td><%= link_to 'Show', person %></td> <td><%= link_to 'Edit', edit_person_path(person) %></td> <td><%= link_to 'Destroy', person, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> <br /> <%= link_to 'New person', new_person_path %>
これで、再度 peopleのリスト画面を見ると、ID、作成日時、更新日時が表示される。特にサーバの再起動は必要ない。
5.テーブルの結合
people と organization テーブルを結合して、people を表示するときに、orgIdではなく、組織名を表示するようにしてみる。また、新規作成時に所属をidではなく、選択から選べるようにもする。
まず、Person と Organization との関係をモデルに定義する。Person と Organization は 多対1なので、Personモデルに、Organization に所属している belongs_to を定義する。このとき、外部キーとして orgId を使うことを定義。(organization_id とかいうカラム名だったら明示なので不要??)
["app/models/person.rb"] class Person < ActiveRecord::Base belongs_to :organization, :foreign_key => "orgId" end
Organization のほうに Person を持ってるよという、has_many を定義してもよいが、今回は使わないのでパス。とりあえず、これで、person.organization.name で関連する組織名をとってこれるようになった。
次は、表示部分の変更。app/views/people/index.html.erb を以下のように変更。
<h1>Listing people</h1> <table> <tr> <th>id</th> <th>Name</th> <th>組織</th> <!-- ← 修正 --> <th>作成日時</th> <th>更新日時</th> </tr> <% for person in @people %> <tr> <td><%=h person.id %></td> <td><%=h person.name %></td> <td><%=h person.organization.name %></td> <!-- ← 修正 --> <td><%=h person.created_at %></td> <td><%=h person.updated_at %></td> <td><%= link_to 'Show', person %></td> <td><%= link_to 'Edit', edit_person_path(person) %></td> <td><%= link_to 'Destroy', person, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> <br />
orgIdを表示していたところを、ヘッダを「組織名」、値を person.organization.name を表示するように修正。これで、関連する組織名が表示されます。
次は、新規追加時に組織名を選択できるように修正。
app/views/people/new.html.erb を変更。
<h1>New person</h1> <%= error_messages_for :person %> <% form_for(@person) do |f| %> <p> <b>Name</b><br /> <%= f.text_field :name %> </p> <p> <b>Orgid</b><br /> <!-- 以下を追加 --> <%= select("person", "orgId", Organization.find(:all).collect {|e| [ e.name, e.id ] }) %> </p> <p> <%= f.submit "Create" %> </p> <% end %> <%= link_to 'Back', people_path %>
<%= f.text_field :orgId %> とかあるところを削除して、上記の例の1文に書き換える。同様の修正を app/views/organizations/edit.html.erb に行えば完了。
Webブラウザからアクセスして、新規追加をするときに 組織名を選択できるようになる。