LoginSignup
153
143

More than 5 years have passed since last update.

Railsで規約に沿わない古いデータを扱う

Posted at

背景

railsではrails g scaffoldなどでModelを作成すれば、
自動的にidが付与されます。
しかもprimary_keyでauto_incrementでかつindexも張られるので、
普段はidを気にする必要はありません。

railsを使い、自分でデータ構造を決める場合はrailsの流儀に則った方が楽で、問題も起こりません。
しかし、古いデータを活用した場合、流儀にそぐわない事もあり得ます。

今回は規約に沿わない場合の対応について大きく分けて2つの場合について説明します。
なお環境は以下の物で検証しています。
* ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0]
* Rails 4.0.0
* composite_primary_keys (6.0.0)

主キーがidではない

主キーがidではない場合として、
具体的に商品(Item)テーブルの主キーが文字列である場合を考えます。

主キーがidで無い場合のポイントは
create_tabelid: falseです。
これによってidが自動生成されることはなくなります。
他のオプションでprimary_keyと言う物もありますが、
これはinteger型で名前をidから変更するだけの物です。

下記の例ではnull: falseとする事でnot nullな制約を追加し、
add_index :items, :code, unique: trueでユニーク制約を定義しています。

Migrationファイル

class CreateItems < ActiveRecord::Migration

  def change
    create_table :items, id: false do |t|
      # 商品コード
      t.string :code, limit: 8, null: false
      # 商品名
      t.string :name
    end
    add_index :items, :code, unique: true
  end

end

モデル側にはself.primary_keyで主キーを設定します。
これによってfindメソッドで主キーによる取得が可能になります。

Item Model

class Item < ActiveRecord::Base
  # 主キー設定
  self.primary_key = :code
end

複合主キーを扱いたい

次は複合主キーを扱う場合についてです。
具体的に先ほど使った商品(Item)について、
各商品に任意のタグ(Tag)がついているとします。
タグは商品コード(item_code)とタグコード(code)の複合主キーを持っています。

railsで複合主キーを扱う場合、
composite_primary_keysのgemを使用します。
composite_primary_keysの準備としてGemfilegem 'composite_primary_keys'を追加しbundle installします。

Migrationファイルのポイントは、
:item_code:codeの組み合わせがユニークである制約の定義です。
複合主キーがユニークである制約はadd_index :tags, [:item_code, :code], unique: trueだけでも十分です。
複合主キーが2つ3つと長くなる場合、インデックス名が長すぎてエラーが発生する事もあるので、
その場合はnameでインデックス名を指定してあげましょう。

Migrationファイル

class CreateTags < ActiveRecord::Migration
  def change
    create_table :tags, id: false do |t|
      # 商品コード
      t.string :item_code, limit: 8
      # タグコード
      t.string :code, limit: 8
      # タグ名称
      t.string :name
    end
    add_index :tags, [:item_code, :code], unique: true, name: 'composite_index'
  end
end

モデルに関しては、
先ほどはself.primary_keyを使いましたが、
複合主キーの場合はself.primary_keysを使います。
これによってTag.find 'item_code', 'code'と、
findメソッドで複合主キーを扱うことが可能になります。

また、foreign_keyによって、どのカラムが外部キーなのかを指定することで、
関連を定義することが可能です。

Tag Model

class Tag < ActiveRecord::Base
  # 主キー設定
  self.primary_keys = :item_code, :code

  # 商品との関連
  belongs_to :item, foreign_key: :item_code
end

Item Model

class Item < ActiveRecord::Base
  # 主キー設定
  self.primary_key = :code

  # タグとの関連
  has_many :tags, foreign_key: :item_code
end

問題

composite_primary_keysによって複合主キーを取り扱うことが出来るようになりました。
しかし、少し問題が発生する場合もあります。
それはModelに対してto_jsonを呼び出した場合などで、以下のエラーが発生します。

TypeError: ["item_code", "code"] is not a symbol

根本ではserializable_hashを呼び出すところでのエラーなので、
モデルでserializable_hashをオーバーライドする事で解決します。

def serializable_hash(options={})
  options = {
    :only => [:item_code, :code, :name_code]
  }.update(options)
  super(options)
end
153
143
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
153
143