Foreigner
概要
Foreigner は migrations に外部キー制約を(付加|削除)する為のメソッドをちょこっと提供するgem。下記のDBアダプタをサポートしている。
- mysql2
- postgres
- sqlite (foreign key methods are a no-op)
インストール
Gemfileに追加:
gem 'foreigner'
APIの例
Foreigner は2つの methods を migrations に追加する。
add_foreign_key(from_table, to_table, options)
remove_foreign_key(from_table, options)
(Optionsの詳細は connection_adapters/abstract/schema_statements.rb
に記載):
本文末に記載する。
modelの例
class Comment < ActiveRecord::Base
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments, dependent: :delete_all
end
#Post has many Commnets.
#Postを削除すると、それに紐づくCommentsもすべて削除される。
この場合、CommentにPostへの外部キーを設ける。
migrationの例
migrationにadd_foreign_key
を追記。
add_foreign_key(:comments, :posts)
migrationで :dependent
オプションを記述すると、modelでの has_many
でdependent:
オプションの代わりになる。
add_foreign_key(:comments, :posts, dependent: :delete)
column:
オプションで外部キーとなるカラム名を指定する。
add_foreign_key(:comments, :posts, column: 'article_id')
さらに、name:
オプションで制約に制約名称を付与できる。
add_foreign_key(:comments, :posts, name: 'comment_article_foreign_key')
外部キー制約を削除するときには、:name
でつけた制約名称も指定する事。
remove_foreign_key(:comments, name: 'comment_article_foreign_key')
テーブルメソッドの変更
Foreigner はActiverecordのcreate_table
と change_table
にメソッドを追加する。
foreign key
の追加
create_table :products do |t|
t.string :name
t.integer :factory_id
t.foreign_key :factories
#t.foreign_key :外部参照先のmodel名
end
change_table :comments do |t|
t.foreign_key :posts, dependent: :delete
end
不要な外部キーを削除:
change_table :comments do |t|
t.remove_foreign_key :users
end
Database-specific options
個別のDBMSのオプション指定には、foreignerは対応する予定はない。 :options
にて各自直接記述する事。
add_foreign_key(:comments, :posts, options: 'ON UPDATE DEFERRED')
ライセンス
下記の通り。
Copyright (c) 2012 Matthew Higgins, released under the MIT license
foreigner/connection_adapters/abstract/schema_statements.rb
module Foreigner
module ConnectionAdapters
module SchemaStatements
def self.included(base)
base::AbstractAdapter.class_eval do
include Foreigner::ConnectionAdapters::AbstractAdapter
end
end
end
module AbstractAdapter
def create_table(table_name, *args, &block)
definition = nil
super do |td|
definition = td # This is my trick to get the definition
block.call(td) unless block.nil?
end
definition.foreign_keys.each do |to_table, options_list|
options_list.each do |options|
add_foreign_key(table_name, to_table, options)
end
end
end
def supports_foreign_keys?
false
end
# Checks to see if a foreign key exists on a table for a given constraint.
#
# # Check a foreign key exists
# foreign_key_exists?(:suppliers, :companies)
#
# # Check a foreign key with a custom name exists
# foreign_key_exists?(:suppliers, name: "fk_company_id")
#
# # Check a foreign key on a column
# foreign_key_exists?(:suppliers, column: "company_id")
#
def foreign_key_exists?(table_name, options)
end
# Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
#
# The foreign key will be named after the from and to tables unless you pass
# <tt>:name</tt> as an option.
#
# ===== Examples
# ====== Creating a foreign key
# add_foreign_key(:comments, :posts)
# generates
# ALTER TABLE `comments` ADD CONSTRAINT
# `comments_post_id_fk` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
#
# ====== Creating a named foreign key
# add_foreign_key(:comments, :posts, name: 'comments_belongs_to_posts')
# generates
# ALTER TABLE `comments` ADD CONSTRAINT
# `comments_belongs_to_posts` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
#
# ====== Creating a cascading foreign_key on a custom column
# add_foreign_key(:people, :people, column: 'best_friend_id', dependent: :nullify)
# generates
# ALTER TABLE `people` ADD CONSTRAINT
# `people_best_friend_id_fk` FOREIGN KEY (`best_friend_id`) REFERENCES `people` (`id`)
# ON DELETE SET NULL
#
# === Supported options
# [:column]
# Specify the column name on the from_table that references the to_table. By default this is guessed
# to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
# as the default <tt>:column</tt>.
# [:primary_key]
# Specify the column name on the to_table that is referenced by this foreign key. By default this is
# assumed to be "id".
# [:name]
# Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
# [:dependent]
# If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
# If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
# [:options]
# Any extra options you want appended to the foreign key definition.
def add_foreign_key(from_table, to_table, options = {})
end
# Remove the given foreign key from the table.
#
# ===== Examples
# ====== Remove the suppliers_company_id_fk in the suppliers table.
# remove_foreign_key :suppliers, :companies
# ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
# remove_foreign_key :accounts, column: :branch_id
# ====== Remove the foreign key named party_foreign_key in the accounts table.
# remove_foreign_key :accounts, name: :party_foreign_key
def remove_foreign_key(from_table, options)
end
# Return the foreign keys for the schema_dumper
def foreign_keys(table_name)
[]
end
end
end
end