LoginSignup
33
21

More than 5 years have passed since last update.

railsでdb:migrateした後railsプロセス再起動が必要な理由

Posted at

理由

  • モデルがカラムの情報をキャッシュして持っているから
    • 間違ってたら突っ込み希望です

参照サイト

db:migrate後のモデル操作例

  • 以下のようなコードを用意する
# db/migrate/20140306063809_create_products.rb
class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t| 
      t.string :name

      t.timestamps
    end 
  end 
end

# app/models/product.rb
class Product < ActiveRecord::Base    
  attr_accessible :name
end
  • db:migrateする
  • Productデータを用意しておく
  • Productにhogeカラムを追加する
# db/migrate/20140306065532_add_hoge_to_product.rb
class AddHogeToProduct < ActiveRecord::Migration
  def change
    Product.first
    add_column :products, :hoge, :string
    Product.first.update_attributes! hoge: 'muga'
  end 
end

# app/models/product.rb
class Product < ActiveRecord::Base
  attr_accessible :name, :hoge
end
  • db:migrateする
  • Product.firstを確認する => hogeがnil
    • 最初のProduct.firstをなくした場合にはhogeはmuga => 初回参照時にDB参照かな
    • 最初のProduct.firstをなくしてinitializerでProductモデルを利用した場合はhogeがnil
    • Product.reset_column_informationをしてupdate_attributes!するとhogeはmuga
    • config.cache_classes = trueにするとdevelopmentでrails sした環境でも確認可能

ちょっとコードを追ってみる

  • デバッグ中心でしか見てないけど、、、

  • update_attributes!の中を追ってみるとsave!でcolumnで持っていないカラムは無視してそう

# activerecord-3.2.17/lib/active_record/persistence.rb

# save!で呼び出されている
def update(attribute_names = @attributes.keys)
  attributes_with_values = arel_attributes_values(false, false, attribute_names) # ここで保持していないカラムは無視
  return 0 if attributes_with_values.empty?
  klass = self.class
  stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values) # updateのsqlクエリ生成
  klass.connection.update stmt
end 
# activerecord-3.2.3/lib/active_record/attribute_methods.rb
...
    def column_for_attribute(name)
      self.class.columns_hash[name.to_s] # カラム持ってないとnil
    end
...
    def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
      attrs      = {}
      klass      = self.class
      arel_table = klass.arel_table

      attribute_names.each do |name|
        if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) # column_for_attributeでnilになると何もしない

          if include_readonly_attributes || !self.class.readonly_attributes.include?(name)

            value = if klass.serialized_attributes.include?(name)
                      @attributes[name].serialized_value
                    else
                      # FIXME: we need @attributes to be used consistently.
                      # If the values stored in @attributes were already type
                      # casted, this code could be simplified
                      read_attribute(name)
                    end

            attrs[arel_table[name]] = value
          end
        end
      end

      attrs
    end
  • カラム初アクセスの時にDB接続にいってあとは保持しているっぽい
# activerecord-3.2.17/lib/active_record/model_schema.rb 

# Returns an array of column objects for the table associated with this class.
def columns
  @columns ||= connection.schema_cache.columns[table_name].map do |col|
    col = col.dup
    col.primary = (col.name == primary_key)
    col
  end
end 
# activerecord-3.2.17/lib/active_record/connection_adapters/schema_cache.rb

module ActiveRecord
  module ConnectionAdapters  
    class SchemaCache
      attr_reader :primary_keys, :tables
      attr_reader :connection

      def initialize(conn)
        @connection = conn
        @tables     = {}

        @columns = Hash.new do |h, table_name|
          h[table_name] = connection.columns(table_name, "#{table_name} Columns")
        end
        ...
      end
...
      # Get the columns for a table
      def columns(table = nil)
        if table
          @columns[table]
        else
          @columns
        end
      end
...
    end
  end
end 
33
21
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
33
21