Rails 4 迁移:Mysql2::错误:数据对于列'xxxx'来说太长



这是我的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中删除了有关此的任何内容?

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

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

在版本4中,Rails默认开启严格模式。这就是为什么您在Rails 3中看到不同的行为。这个就是您可能需要的行为。静默截断数据几乎总是不好的,并且会导致用户非常困惑的行为。

如果你真的想截断数据,你可以关闭严格模式或使用before过滤器:

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

我偶然发现了这篇几天前写的文章。

主要的兴趣点是:

…我最近从rails 3.2升级到rails 4.0。他们实施了我找不到任何关于ActiveRecords的重大变化

  • mysqlmysql2连接默认设置为SQL_MODE=STRICT_ALL_TABLES,以避免静默数据丢失。选项中指定strict: false可以禁用此功能database.yml

这似乎解释了为什么当您返回到Rails 3时停止接收错误。你可以在MySQL连接适配器模块的选项中看到这一点,它看起来像是在2012年5月为4.1.2发行候选版本添加的(如果我没看错标签的话)。

这个人通过解决了他们的问题….

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

我学习了在数据库中设置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

最新更新