【问题标题】:Rails 4 Migration: Mysql2::Error: Data too long for column 'xxxx'Rails 4 迁移:Mysql2::Error: Data too long for column 'xxxx'
【发布时间】:2014-08-10 04:51:56
【问题描述】:

这是我的 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

我为“名称”列设置了限制字符串。

然后,在控制台中:

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

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

【问题讨论】:

  • 对于最后一个问题,可能取决于您创建User 的用例。也许最好添加一个验证器来检查 :name 的长度,如果它结束太长,则向用户显示错误?
  • 验证可能是要走的路,因为用户应该知道他们的名字不能超过 6 个字符。否则当他们输入“Jonathan”时他们可能会感到惊讶,但当他们查看他们的个人资料时,他们的名字被列为“Jonath”。
  • 谢谢,我知道更好的方法是添加一些验证。但我只是想弄清楚 Rails 4 中发生了什么变化,而且我有一些模型对用户是不可见的,所以在这种情况下,验证无法帮助我。
  • 我明白了。从您所说的看来,Rails 4 中发生了变化......如果该列对用户不可见,则可以在before_save 回调函数中截断它。

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


【解决方案1】:

您看到的是 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

【讨论】:

  • 谢谢,很有帮助!救生员
  • 如果内容包含非 ASCII 字符,如 ç,则截断字符长度是不够的。 Rails 6 添加了 String#truncate_bytes 以在不破坏多字节字符或字素簇的情况下将字符串截断到最大字节大小?
【解决方案2】:

偶然发现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 的选项中看到这一点,看起来它是 4.1.2 版本的 added way back in May 2012候选人(如果我正确阅读了标签)。

此人通过...[修复] 代码解决了他们的问题,使其具有适当的字段长度,或手动截断数据...

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

【讨论】:

  • 这个答案不仅指向一篇文章,它实际上解释了真正的问题是什么,阅读它之后,您还可以看到为什么在迁移时自己切片数据同样糟糕。很好的答案@paul-richter
【解决方案3】:

我了解在 database.yml 中设置 strict: false 为时已晚。 在尝试对所有输入进行设置 maxlength 255 等各种操作后,我有一个解决方法。这可能会对繁重的写入工作负载产生性能影响。适合您的普通 CMS。

在此处转储此代码以防万一,无论出于何种原因,您现在或将来都无法禁用 mysql 中的严格模式。

基本上,before_validation - 如果值是脏的和 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-06
    • 2019-07-28
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 2020-06-27
    • 1970-01-01
    • 2019-07-01
    相关资源
    最近更新 更多