Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

背景

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
k-shogo
勇者シリーズを見て育ち、人工知能に夢を見た。 大学時代は人工知能を研究。 現在はrailsエンジニア。 好きなテキストエディタはvim 好きなvimプラグインはunite 好きなコマンドはxargs
http://k-shogo.github.io/
recruitmp
結婚・カーライフ・進学の情報サイトや『スタディサプリ』などの学びを支援するサービスなど、ライフイベント領域に関わるサービスを提供するリクルートグループの中核企業
http://www.recruit-mp.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした