フォーチュンサモナーズ
«前の日記(2005-02-17) 最新 次の日記(2005-03-01)» 編集

Don'tStopMusic

2003|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|12|
2006|01|02|03|04|05|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|08|09|10|

カテゴリ別 2003年 | 2004年 | 2005年 | 2006年 | 2007年 | 2008年

知り合いサイト: よんだもの / 暴想 / Linuxでやる夫 / 新宿Vipper / 僕だけが幸せになればいいのに。


2005-02-26

_ [Ruby] MT のデータベースに ActiveRecord を使ってみる このエントリーを含むブックマーク

ActiveRecord の勉強をするのに何か良い題材がないかと考えていましたが、それなりに複雑でそれなりに知っている MovableType のデータベースにします。

環境

MT は mt-load.cgi でテーブル作成済み。

  • Movable Type 3.151 ja
  • mysql Ver 12.22 Distrib 4.0.22, for pc-linux-gnu (i686)
  • ruby 1.8.2 (2004-12-25) [i686-linux]
  • activerecord 1.7.0

_ [Ruby][ActiveRecord] mt_author へアクセス このエントリーを含むブックマーク

mt-load.cgi を叩くと初期ユーザが作成されますので、まずはそのデータを Ruby+ActiveRecord で表示するところから。

いきなりコードを書いてみます。

#!/usr/bin/env ruby
require 'rubygems'
require_gem 'activerecord'
require 'pp'
require 'logger'
 
ActiveRecord::Base.logger = Logger.new("debug.log")
ActiveRecord::Base.establish_connection(
  :adapter  => "mysql",
  :host     => "localhost",
  :username => "mt",
  :password => "mt",
  :database => "mt"
)
 
module MT
  class Author < ActiveRecord::Base
  end #Author
end #MT
 
pp MT::Author.find(1)

意図としては

  • mt_author は MT::Author にマッピング
  • 初期ユーザ(id=1)を取得して表示

となります。で、これを実行すると、

$ ruby sample.rb 
/usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/abstract_adapter.rb:377:in `log': Table 'mt.authors' doesn't exist: SELECT * FROM authors WHERE id = 1  LIMIT 1 (ActiveRecord::StatementInvalid)
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:95:in `execute'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:173:in `select'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:73:in `select_all'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:337:in `find_by_sql'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:330:in `find_all'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:346:in `find_first'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:287:in `find'
        from sample.rb:21

などと怒られます。エラーメッセージにも表示されていますが、debug.log に実行しようとした SQL が記録されますので、それを見てみます。

$ tail -1 debug.log
  MT::Author Load (0.000000)   Table 'mt.authors' doesn't exist: SELECT * FROM authors WHERE id = 1 LIMIT 1

データベース mt には authors という名前のテーブルはないと言われています。これは ActiveRecord には、暗黙的なクラス名とテーブル名の命名規則があるためです。上のコードでは Author クラスを定義しましたので、authors テーブルとマッピングしようとします。

マッピングの命名規則
クラス名
単数形
テーブル名
複数形

mt_author というテーブル名は変えられませんので、Ruby のコードの方で何とかしないといけません。何とかするには ActiveRecord::Base.table_name を使います。上のコードの Author クラス定義部分だけを抜き出してみます。

module MT
  class Author < ActiveRecord::Base
    def self.table_name
      "mt_author"
    end
  end #Author
end #MT

再び実行してみるとまだエラーです。さっきとはエラーメッセージが違っていますね。

$ ruby sample.rb 
/usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/abstract_adapter.rb:377:in `log': Unknown column 'id' in 'where clause': SELECT * FROM mt_author WHERE id = 1  LIMIT 1 (ActiveRecord::StatementInvalid)
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:95:in `execute'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:173:in `select'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:73:in `select_all'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:337:in `find_by_sql'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:330:in `find_all'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:346:in `find_first'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:287:in `find'
        from sample.rb:24
 $ tail -1 debug.log
  MT::Author Load (0.000000)   Unknown column 'id' in 'where clause': SELECT * FROM mt_author WHERE id = 1 LIMIT 1

これは、ActiveRecord はデフォルトでは主キーのカラム名を id とみなすためです。mt_author のスキーマを見てみましょう。

mysql> show fields from mt_author;
+-----------------------------+--------------+------+-----+---------+----------------+
| Field                       | Type         | Null | Key | Default | Extra          |
+-----------------------------+--------------+------+-----+---------+----------------+
| author_id                   | int(11)      |      | PRI | NULL    | auto_increment |
| author_name                 | varchar(50)  |      | MUL |         |                |
| author_type                 | tinyint(4)   |      |     | 0       |                |
| author_nickname             | varchar(50)  | YES  |     | NULL    |                |
| author_password             | varchar(60)  |      |     |         |                |
| author_email                | varchar(75)  |      | MUL |         |                |
| author_url                  | varchar(255) | YES  |     | NULL    |                |
| author_can_create_blog      | tinyint(4)   | YES  |     | NULL    |                |
| author_can_view_log         | tinyint(4)   | YES  |     | NULL    |                |
| author_hint                 | varchar(75)  | YES  |     | NULL    |                |
| author_created_by           | int(11)      | YES  |     | NULL    |                |
| author_public_key           | text         | YES  |     | NULL    |                |
| author_preferred_language   | varchar(50)  | YES  |     | NULL    |                |
| author_remote_auth_username | varchar(50)  | YES  |     | NULL    |                |
| author_remote_auth_token    | varchar(50)  | YES  |     | NULL    |                |
+-----------------------------+--------------+------+-----+---------+----------------+
15 rows in set (0.00 sec)

主キーは author_id ですね。これもデータベースの方は変更できませんから、Ruby+ActiveRecord 側でなんとかしなくてはなりません。で、なんとかするのがこのコードです。

ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore

主キーの命名規則を指定します。author テーブルの主キーは author_id でしたので、「テーブル名_id」 という規則であるとしています。

これでまた実行すると、、、

$ ruby sample.rb 
#<MT::Author:0x2af7c7a4
 @attributes=
  {"author_remote_auth_token"=>nil,
   "author_name"=>"Melody",
   "author_can_create_blog"=>"1",
   "author_type"=>"1",
   "author_nickname"=>nil,
   "author_can_view_log"=>"1",
   "author_created_by"=>nil,
   "author_public_key"=>nil,
   "author_id"=>"1",
   "author_password"=>"0osHZ.scFVmok",
   "author_email"=>"",
   "author_remote_auth_username"=>nil,
   "author_url"=>nil,
   "author_hint"=>nil,
   "author_preferred_language"=>"ja"}>

ようやく上手くいきました。

以上で見てきましたように、既に存在しているデータベースに対して ActiveRecord を使って OR マッピングをする場合には、ActiveRecord が想定するデフォルトの命名規則には従っていないことが多いため、定義やオプション設定をする必要があります。どんなパターンに対応できるかは、今のところ ActiveRecord の API で調べるしかないようです。

最終的なコード

#!/usr/bin/env ruby
require 'rubygems'
require_gem 'activerecord'
require 'pp'
require 'logger'
 
ActiveRecord::Base.logger = Logger.new("debug.log")
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
ActiveRecord::Base.establish_connection(
  :adapter  => "mysql",
  :host     => "localhost",
  :username => "mt",
  :password => "mt",
  :database => "mt"
)
 
module MT
  class Author < ActiveRecord::Base
    def self.table_name
      "mt_author"
    end
  end #Author
end #MT
 
pp MT::Author.find(1)
本日のツッコミ(全2件) [ツッコミを入れる]
_ キムキム (2005-05-11 16:30)

残念。。。もうこのページの意味分かりませんから<br>そのページに来た私に切腹。。。

_ MoonWolf (2005-12-19 01:56)

MTとActiveRecordの両方から同時にDBにアクセスするのでなければ、DBの構造を見直してActiveRecordの流儀にしたがうのがよいと思います。

[]

最近のコメント:

  1. MoonWolf (12-19)
  2. キムキム (05-11)

RSS
Creative Commons License
This work is licensed under a Creative Commons License
(note: text only. w/o web design, citations, (re)distributed softwares).