Rails 4迁移:Mysql2 ::错误:对于列&x; xxxx'

时间:2014-06-19 15:17:32

标签: ruby-on-rails ruby-on-rails-4 rails-migrations

这是我的schema.rb

  create_table "users", force: true do |t|
    t.string   "name",       limit: 6
    t.string   "email"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

我为列" name"。

设置了字符串限制

然后,在控制台中:

 user = User.new(name:"1234567890",email:"username@gmail.com")
 user.save!

它引发了错误:

ActiveRecord::StatementInvalid: Mysql2::Error: Data too long for column 'name' at row 1: INSERT INTO `users` (`created_at`, `email`, `name`, `updated_at`) VALUES ('2014-06-19 15:08:15', 'username@gmail.com', '1234567890', '2014-06-19 15:08:15')

但是,当我切换到 rails 3 时。

我发现它截断了字符串" 1234567890 "自动插入" 123456 "进入数据库没有错误。

rails 4

中是否删除了有关此内容的内容?

我应该自己在模型中添加一些截断函数吗?谢谢!

3 个答案:

答案 0 :(得分:17)

您所看到的是MySQL的差异,而不是Rails。默认情况下,MySQL会截断太长的数据,而不是抛出错误。如果将MySQL设置为strict模式,它将抛出错误而不是静默截断数据。

版本4,默认为Rails turns on strict mode。这就是您使用Rails 3看到不同行为的原因。 您可能想要的行为。静默截断数据几乎总是很糟糕,并且可能导致用户的行为非常混乱。

如果确实想要截断数据,您可以turn off strict mode或使用之前的过滤器:

before_save :truncate_username
def truncate_username
  self.username = username.slice(0, 6)
end

答案 1 :(得分:7)

我偶然发现this article只是在几天前写的。

主要关注点是:

  

...我最近从rails 3.2升级到rails 4.0。他们实施了一个   ActiveRecords的主要变化,我发现没有任何提及   除了源和更改日志之外的其他地方。

     
      
  • mysqlmysql2连接默认设置为SQL_MODE=STRICT_ALL_TABLES,以避免无提示数据丢失。   可以通过在您的帐户中指定strict: false来禁用此功能   database.yml
  •   

这似乎可以解释为什么当你恢复到Rails 3时你停止接收错误的原因。你可以在MySQL connection adapter module的选项中看到这一点,看起来它是added way back in May 2012 4.1.2发布候选版(如果我正确阅读标签)。

此人通过 ... [修复]代码解决了问题,要么具有正确的字段长度,要么手动截断数据......

在您的情况下,您可以通过在strict: false中添加database.yml来解决Rails 4中的问题。如果您想自定义截断数据的方式,我同意JKen13579关于before_save回调的建议。否则,从我所看到的,它似乎截断最右边的字符,所以如果这足够,你可以逃脱默认的截断行为。

答案 2 :(得分:0)

我了解到太晚了在database.yml中设置strict: false的知识。 尝试在所有输入上执行各种操作(如设置maxlength 255)后,我有了解决方法。这可能会对繁重的写入工作负载产生性能影响。适合您的平均CMS。

在这种情况下,无论出于何种原因,现在或将来都不能在mysql中禁用严格模式,请在此处转储此代码。

基本上,before_validation-如果该值是dirty和varchar并且> 255,则将其修整。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  before_validation :silent_string_truncate
  def silent_string_truncate
    # For each changed value
    self.changes.each do |key,values|
      # Only worry about non-empty fields, which are longer than 255
      if values.last.blank? || values.last.length < 255
        next
      end

      # And only truncate if its a column on this table
      if self.class.columns_hash[key].blank?
        next
      end

      # And only truncate on string fields (varchar)
      properties = self.class.columns_hash[key]
      if properties.type == :string
        self.send("#{key}=", values.last.slice(0,255))
      end

    end
  end

  # Other global methods here
end