Ruby on Rails (ActiveReocrd) モデルの上限値を取得する
概要
ActiveRecord でモデルの長さを取得する処理のメモ書き。
目的
Simple Form の 設定のファイルを、use
に変えると、lengthバリデーションか、データベースのカラムの長さを設定してくれるらしい。
## Optional extensions # They are disabled unless you pass `f.input EXTENSION_NAME => true` # to the input. If so, they will retrieve the values from the model # if any exists. If you want to enable any of those # extensions by default, you can change `b.optional` to `b.use`. # Calculates maxlength from length validations for string inputs # and/or database column lengths b.optional :maxlength # Calculate minlength from length validations for string inputs b.optional :minlength
これと同じように、モデルのカラム長さを取得したい。
方法
前提
例えば、こんな感じのActiveRecordモデルがあったとする。
class Post < ApplicationRecord validates :title, presence: true, length: {minimum: 1, maximum: 20} validates :content, presence: true, length: {minimum: 1, maximum: 500} end
そして、データベースはこんな感じで定義されているとする。
CREATE TABLE `posts` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `title` varchar(255) DEFAULT NULL, `content` text, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
バリデーションの長さ
バリデーションに指定された長さは、validators_onを使えば取得できる。下記に、rails consoleでの実行結果を記載する。他にも、例があったけど個人的に一番これが好みだった。
Loading development environment (Rails 5.2.4.3) irb(main):001:0> Post.validators_on(:title).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:minimum] => 1 irb(main):002:0> Post.validators_on(:title).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:maximum] => 20 irb(main):003:0> Post.validators_on(:content).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:minimum] => 1 irb(main):004:0> Post.validators_on(:content).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }.options[:maximum] => 500
このメソッドのソースコードはこちら。
def validators_on(*attributes) attributes.flat_map do |attribute| _validators[attribute.to_sym] end end
SimpleForm ではここで使っている。
def attribute_validators object.class.validators_on(attribute_name) end
データベースカラムの長さ
次に、データベースからカラムの長さを取得するには、column_for_attribute を使う。下記に、rails consoleでの実行結果を記載する。長さは、limitを使う。typeを使えば、型も取得できる。
irb(main):040:0> Post.column_for_attribute(:title).type => :string irb(main):041:0> Post.column_for_attribute(:title).limit => 255 irb(main):043:0> Post.column_for_attribute(:content).type => :text irb(main):044:0> Post.column_for_attribute(:content).limit => 65535
column_for_attribute の他に、type_for_attributeでも取得できる。これも、同様にtypeを使えば型、limitで長さを取得する。
irb(main):009:0> Post.type_for_attribute('title').type => :string irb(main):010:0> Post.type_for_attribute('title').limit => 255 irb(main):011:0> Post.type_for_attribute('content').type => :text irb(main):012:0> Post.type_for_attribute('content').limit => 65535
ソースコードはこちら。
- activerecord/lib/active_record/model_schema#type_for_attribute
- activerecord/lib/active_record/model_schema#column_for_attribute
# ActiveRecord / ModelSchema # l.392 ~ l401 def type_for_attribute(attr_name, &block) attr_name = attr_name.to_s attr_name = attribute_aliases[attr_name] || attr_name if block attribute_types.fetch(attr_name, &block) else attribute_types[attr_name] end end # l.416 ~ l421 def column_for_attribute(name) name = name.to_s columns_hash.fetch(name) do ConnectionAdapters::NullColumn.new(name) end end
Simple Form では、どちらでも対応できるようにか、どっちのメソッドも使っている。
ソースコードはこちら。
def find_attribute_column(attribute_name) if @object.respond_to?(:type_for_attribute) && @object.has_attribute?(attribute_name) @object.type_for_attribute(attribute_name.to_s) elsif @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name) @object.column_for_attribute(attribute_name) end end
このfind_attribute_column
で取得し、Inputs/Baseクラス内で取得したインスタンスから、limitで上限値を取得する。これはSimpleForm でも同じ。
あとがき
DBのカラムの最大値が取れるのは、使えるところがありそう。あるはず。入力のmaxlength
の一部だけこの値を指定するとか…。