这是我的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
中是否删除了有关此内容的内容?我应该自己在模型中添加一些截断函数吗?谢谢!
答案 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的主要变化,我发现没有任何提及 除了源和更改日志之外的其他地方。
mysql
和mysql2
连接默认设置为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