0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ActiveRecord::Base.connection.column_exists? メソッドのユースケース

Posted at

はじめに

今回はActiveRecord::Base.connectionのcolumn_exists?メソッドの内部実装とユースケースについて書いてみたいと思います。

環境

Ruby 2.6.6
Rails 5.2.6

内部実装

まずは実装箇所を調べてみます。

$ bundle exec rails c

$ ActiveRecord::Base.connection.method(:column_exists?).source_location
=> ["/bundle/gems/activerecord-5.2.6/lib/active_record/connection_adapters/abstract/schema_statements.rb", 132]

実装箇所はこちらでした。

# Checks to see if a column exists in a given table.
#
#   # Check a column exists
#   column_exists?(:suppliers, :name)
#
#   # Check a column exists of a particular type
#   column_exists?(:suppliers, :name, :string)
#
#   # Check a column exists with a specific definition
#   column_exists?(:suppliers, :name, :string, limit: 100)
#   column_exists?(:suppliers, :name, :string, default: 'default')
#   column_exists?(:suppliers, :name, :string, null: false)
#   column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
#
def column_exists?(table_name, column_name, type = nil, options = {})
  column_name = column_name.to_s
  checks = []
  checks << lambda { |c| c.name == column_name }
  checks << lambda { |c| c.type == type } if type
  column_options_keys.each do |attr|
    checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
  end

  columns(table_name).any? { |c| checks.all? { |check| check[c] } }
end

第一引数にテーブル名を、第二引数にカラム名を、それ以降はオプションで指定できるようになっていました。

column_options_keysはprivateメソッドになっており、

def column_options_keys
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
end

マイグレーション時に指定できるオプション(7種類もあったのか、、、)を配列で返すメソッドになっていました。

使い方とユースケース

使い方

実際に挙動を確認してみました。前提として、emailというカラムを持つUserモデルが定義されていて、default値が「""」、null: falseが設定されているものとします。

# emailカラムが定義されているかどうか
$  ActiveRecord::Base.connection.column_exists?(:users, :email)
=> true

# passwordカラムは定義されていないのでfalseが返る
$ ActiveRecord::Base.connection.column_exists?(:users, :password)
=> false

# emailカラムはstring型なので、第三引数にintegerを渡すとfalseが返る
$ ActiveRecord::Base.connection.column_exists?(:users, :email, :integer)
=> false

# emailカラムのデフォルト値は "" なので、trueが返る
$ ActiveRecord::Base.connection.column_exists?(:users, :email, :string, default: "")
=> true

# emailカラムは null: false なので、trueが返る
$ ActiveRecord::Base.connection.column_exists?(:users, :email, :string, null: false)
=> true

# default: "" と null: false の両方を指定してもOK
$ ActiveRecord::Base.connection.column_exists?(:users, :email, :string, null: false)
=> true

実務上で第三引数まで設定して呼び出すことは稀かと思いますが、柔軟なインターフェイスになっている印象です。

ユースケース

個人的には、フロントから渡ってきたパラメータに応じて、走査するテーブルを変えたい時に使えるかなと思っています。

例えば、複数のテーブルを用いて表示されているデータをソートしたい、となった場合、ソート対象のデータによっては走査するテーブルを分岐させなくてはいけないかもしれません、そのような場合に、column_exists?メソッドを用いて分岐を行うのがアリなのではないかと思いました。

まとめ

「ActiveRecord完全に理解した」い。(願望)

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?