読者です 読者をやめる 読者になる 読者になる

Rails でPostgreSQLの配列型を試す

Ruby

今回の仕事でPostgreSQLの配列(array)型を使えると便利なので試してみました。

Migration

Migration で Rails標準の型以外を使うには型の部分を文字列で指定します。

class CreateTodos < ActiveRecord::Migration
  def self.up
    create_table :todos do |t|
      t.date :due
      t.string :task
      t.column :opts, "integer[]"
      t.timestamps
    end
  end

  def self.down
    drop_table :todos
  end
end

rake db:migrateすると無事に 配列型 カラムが出来ました。

> \d todos
                                     Table "public.todos"
   Column   |            Type             |                     Modifiers                      
------------+-----------------------------+----------------------------------------------------
 id         | integer                     | not null default nextval('todos_id_seq'::regclass)
 due        | date                        | 
 task       | character varying(255)      | 
 opts       | integer[]                   | 
 created_at | timestamp without time zone | 
 updated_at | timestamp without time zone | 

ActiveRecordでのアクセス

配列カラムはStringとしてアクセスできます。

>> t = Todo.find(1)
=> #<Todo id: 1, due: "2008-12-12", task: "Test1", opts: "{1,10,100}", created_at: "2008-12-12 05:21:26", updated_at: "2008-12-12 05:21:26">
>> t.opts
=> "{1,10,100}"
>> t.opts.class
=> String

>> t.opts = "{2,20,200}"
=> "{2,20,200}"
>> t.save
=> true
>> t = Todo.find(1)
=> #<Todo id: 1, due: "2008-12-12", task: "Test1", opts: "{2,20,200}", created_at: "2008-12-12 05:21:26", updated_at: "2008-12-12 05:32:45">

配列として扱えるようにする

てきとうなコードを書いてみました。

class Todo < ActiveRecord::Base
  def opts
    v = read_attribute(:opts)
    v ? v.sub(/\{(.*)\}/,'\1').split(',').map{|e| e.to_i} : nil
  end
  
  def opts=(v)
    write_attribute :opts, v ? '{' + v.join(',') + '}' : nil
  end
end
% script/console
>> t = Todo.find(1)
=> #<Todo id: 1, due: "2008-12-12", task: "Test1", opts: "{1,2,3,4}", created_at: "2008-12-12 05:21:26", updated_at: "2008-12-12 05:43:18">
>> t.opts
=> [1, 2, 3, 4]

>> t.opts=[10,100,1000]
=> [10, 100, 1000]
>> t.save
=> true
>> t = Todo.find(1)
=> #<Todo id: 1, due: "2008-12-12", task: "Test1", opts: "{10,100,1000}", created_at: "2008-12-12 05:21:26", updated_at: "2008-12-12 06:16:05">
>