Rails migration 和model 的validations检查冲突时

    今天,干活的时候,遇到这样的一个问题,我们的一个系统有很多版本,有些客户由于种种原因始终使用的是比较早期的版本,姑且说2.03版本吧。

有这样一个migration

class AddFreqAnalyzers < ActiveRecord::Migration
#055_add_freq_analyzers.rb
  def self.up
    add_column :analyzers, :start_freq, :int
    add_column :analyzers, :stop_freq, :int
    startFreq=ConfigParam.get_value(ConfigParam::StartFreq);
    stopFreq=ConfigParam.get_value(ConfigParam::StopFreq);
    puts startFreq
    puts stopFreq
    Analyzer.find(:all).each { |a|
      a.start_freq=startFreq;
      a.stop_freq = stopFreq;
      a.save
    }
    1;
  end

  def self.down
    remove_column :analyzers, :start_freq
    remove_column :analyzers, :stop_freq
  end
end

主要的作用是添加了两个字段和给这个字段赋值。

然后,我们2.06版本的时候给一个model加了一个字段,很正常的我们给这个字段加了validation的保存检查。然后,我们测试升级

class AddWebApiUrl < ActiveRecord::Migration
#085_add_web_api_url.rb
  def self.up
    add_column :analyzers, :api_url, :string,  :null => true
  end

  def self.down
    remove_column :analyzers, :api_url
  end
end

并且在model中增加检查如下:
  validates_format_of     :api_url,
                          :with =>/(^(rubyscript:|http:\/\/|https:\/\/)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)|^about:blank$|^$/ix  


然后,一直没有什么问题,包括正常的升级。然而,在一个用户那里他们的系统是2.02的那么系统升级的话,就会把migration从040一直运行。结果报了错,如下:

引用
== 55 AddFreqAnalyzers: migrating =============================================
-- add_column(:analyzers, :start_freq, :int)
   -> 0.0136s
-- add_column(:analyzers, :stop_freq, :int)
   -> 0.0033s
2000000.0
52000000.0
rake aborted!
undefined method `api_url' for #<Analyzer:0x2aaaae4533b8>



错误提示是没有定义api_url方法,实际上055版本的migrate根本没有api_url的字段,只有到085才有,可是代码里已经有了validation的检查,所以,升级过程就出错了。这样的错误还真奇怪了一阵子,最后,怎么解决的呢:

  validates_format_of     :api_url,
                          :with =>/(^(rubyscript:|http:\/\/|https:\/\/)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)|^about:blank$|^$/ix,
                          :if => Proc.new { |record| record.respond_to? :api_url}	


这就和
  validates_format_of     :email,
                          :with => /(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z|^$)/i,
                          :if => Proc.new { |analyzer|analyzer.email.length >0 }   

一样表示只有特定条件才进行检查。


下面是同样的例子:

class CreateVendors < ActiveRecord::Migration
  def self.up
    create_table :vendors do |t|
      t.string :name
      t.string :email
    end    a = Vendor.new(:name => “Fred Flinstone”, :email => “[email protected]”)
    a.save!
    b = Vendor.new(:name => “Barney Ruble”, :email => “[email protected]”)
    b.save
  end
  def self.down
    drop_table :vendors
  end
end


model:

validates_presence_of :rating, :on => :create, :message => "can't be blank"



出错提示:

引用
$ rake db:migrate
(in .... )
== 2 CreateVendors: migrating =======================================
rake aborted!
Validation failed: Rating can't be blank(See full trace by running task with --trace)


解决如下:


  validates_presence_of :rating, :on => :create, :message => "can't be blank",
    :if => Proc.new { |record| record.respond_to? :rating }


原文作者的解释关于respond_to?
引用
responds_to asks the Rails model: “Do you have a method named ‘rating’?”. Because Rails magically creates methods for every column an entity has in the database, Rails will either have that method (because it has been defined in the database), or not (because no such column exists). So we only run the validation if we have the column, avoiding the mess with trying to validate data that doesn’t make sense (yet).

你可能感兴趣的:(Web,Ruby,UP,Rails,ActiveRecord)