LoginSignup
14
15

More than 5 years have passed since last update.

Foreigner 雑記

Last updated at Posted at 2014-04-23

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_manydependent:オプションの代わりになる。

  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_tablechange_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
14
15
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
14
15