読者です 読者をやめる 読者になる 読者になる

紳士なブログ

紳士すぎてすみません

findメソッドに:joinsオプション指定したときの:readonlyオプションについて

findメソッドに:joins指定したときのreadonlyオプションについて
なんとなくは理解にとどまっていたので、今更ながら調べてみる。


activerecord-2.3.12/lib/active_record/base.rb
611 def find(*args)
612   options = args.extract_options!
613   validate_find_options(options)
614   set_readonly_option!(options)
615
616   case args.first
617     when :first then find_initial(options)
618     when :last  then find_last(options)
619     when :all   then find_every(options)
620     else             find_from_ids(args, options)
621   end
622 end
}}}
  • set_readonly_option!をActiveRecord::Base.findメソッド内で呼び出す
    • :joinsオプションを指定し、かつ、:selectオプションを指定していない際にoptions[:readonly] => trueになる
2448 def set_readonly_option!(options) #:nodoc:
2449   # Inherit :readonly from finder scope if set.  Otherwise,
2450   # if :joins is not blank then :readonly defaults to true.
2451   unless options.has_key?(:readonly)
2452     if scoped_readonly = scope(:find, :readonly)
2453       options[:readonly] = scoped_readonly
2454     elsif !options[:joins].blank? && !options[:select]
2455       options[:readonly] = true
2456     end
2457   end
2458 end
  • options[:readonly] => trueの場合、recordごとに@readonly => trueとなる
1576 def find_every(options)
1577   include_associations = merge_includes(scope(:find, :include), options[:include])
1578
1579   if include_associations.any? && references_eager_loaded_tables?(options)
1580    records = find_with_associations(options)
1581   else
1582     records = find_by_sql(construct_finder_sql(options))
1583     if include_associations.any?
1584       preload_associations(records, include_associations)
1585     end
1586   end
1587
1588   records.each { |record| record.readonly! } if options[:readonly]
1589
1590  records
1591 end
2893 def readonly!
2894   @readonly = true
2895 end
  • レコードをsaveする際、@readonly == trueだと例外が発生する
2576 def save
2577   create_or_update
2578 end
2925 def create_or_update
2926   raise ReadOnlyRecord if readonly?
2927   result = new_record? ? create : update
2928   result != false
2929 end
2893 def readonly?
2894   defined?(@readonly) && @readonly == true
2895 end

ちなみに、update_allではreadonlyであるかどうかを見ずに直接sqlを発行しているため、:joinsを指定してもレコードの更新を行うことができる

831 def update_all(updates, conditions = nil, options = {})
832   sql  = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} "
833
834   scope = scope(:find)
835
836   select_sql = ""
837   add_conditions!(select_sql, conditions, scope)
838
839   if options.has_key?(:limit) || (scope && scope[:limit])
840     # Only take order from scope if limit is also provided by scope, this
841     # is useful for updating a has_many association with a limit.
842     add_order!(select_sql, options[:order], scope)
843
844     add_limit!(select_sql, options, scope)
845     sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key)))
846   else
847     add_order!(select_sql, options[:order], nil)
848     sql.concat(select_sql)
849   end
850
851   connection.update(sql, "#{name} Update")
852 end