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