这是我的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的重大变化
mysql
和mysql2
连接默认设置为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