在Rails 4中,设置了ActiveRecord及其MySQL适配器,因此如果您尝试将AR模型中的属性保存到MySQL数据库,其中属性字符串长度对于MySQL列限制来说太宽 - - 你会引发异常。
大!这比Rails3好得多,它默默地截断了字符串。
但是,偶尔我有一个属性,我明确希望将其简单地截断为db允许的最大大小,没有例外。我无法找出使用AR执行此操作的最佳/支持方式。
理想情况下应该在设置属性后立即发生,但我会在保存时将其发生。 (这不是一个'验证',因为我从不想提出,只是截断,但也许验证系统是最好的支持方式来做到这一点?)
理想情况下,它会通过AR的内省自动计算db列宽,因此如果db列宽度发生更改(在以后的迁移中),截断限制将相应更改。但如果不可能,我会采用硬编码的截断限制。
理想情况下,这将是适用于任何数据库的通用增强现实代码,但如果没有好办法,我会选择仅适用于MySQL的代码
< / LI>答案 0 :(得分:4)
您可以在使用before_save
或before_validation
插入数据库之前截断数据
请参阅Active Record Callbacks — Ruby on Rails Guides和ActiveRecord::Callbacks
您可以使用MODEL.columns
和MODEL.columns_hash
检索表格中的信息。
见ActiveRecord::ModelSchema::ClassMethods
例如(未经测试):
class User < ActiveRecord::Base
before_save :truncate_col
......
def truncate_col
col_size = User.columns_hash['your_column'].limit
self.your_column = self.your_column.truncate(col_size)
end
end
答案 1 :(得分:2)
我非常确定您可以通过ActiveRecord回调和ConnectionsAdapters的组合来实现这一目标。 ActiveRecord包含几个callbacks,您可以覆盖这些column以在保存流程中的不同点执行特定逻辑。由于在保存时抛出异常,我建议将您的逻辑添加到before_save
方法。使用{{3}} ConnectionAdapter,您应该能够确定要插入的列的限制,尽管字符串与整数的逻辑很可能是不同的等等。在我的脑海中,你可以直接使用它。可能想要实现类似的东西:
class User < ActiveRecord::Base
def before_save
limit = User.columns_hash['attribute'].limit
self.attribute = self.attribute[0..limit-1] if self.attribute.length > limit
end
end
以上示例适用于字符串,但此解决方案应适用于所有连接适配器,假设它们支持limit属性。希望这会有所帮助。
答案 2 :(得分:1)
我想谈几点:
如果your_column
的数据类型为text
,则在Rails 4中,User.columns_hash['your_column'].limit
将返回nil
。如果是int
或varchar
,它将返回一个数字。
MySQL中的文本数据类型的存储限制为64k。如果内容包含ç
之类的非ASCII字符(需要存储多个字节),则仅截断字符长度是不够的。
我最近遇到了这个问题,这是它的修复程序:
before_save :truncate_your_column_to_fit_into_max_storage_size
def truncate_your_column_to_fit_into_max_storage_size
return if your_column.blank?
max_field_size_in_bytes = 65_535
self.your_column = your_column[0, max_field_size_in_bytes]
while your_column.bytesize > max_field_size_in_bytes
self.your_column = your_column[0..-2]
end
end
答案 3 :(得分:0)
这是我自己的自我回答,它在属性集上截断(保存前的方式)。好奇,如果有人有任何反馈。它似乎工作!
# An ActiveRecord extension that will let you automatically truncate
# certain attributes to the maximum length allowed by the DB.
#
# require 'truncate_to_db_limit'
# class Something < ActiveRecord::Base
# extend TruncateToDbLimit
# truncate_to_db_limit :short_attr, :short_attr2
# #...
#
# Truncation is done whenever the attribute is set, NOT waiting
# until db save.
#
# For a varchar(4), if you do:
# model.short_attr = "123456789"
# model.short_attr # => '1234'
#
#
# We define an override to the `attribute_name=` method, which ActiveRecord, I think,
# promises to call just about all the time when setting the attribute. We call super
# after truncating the value.
module TruncateToDbLimit
def truncate_to_db_limit(*attribute_names)
attribute_names.each do |attribute_name|
ar_attr = columns_hash[attribute_name.to_s]
unless ar_attr
raise ArgumentError.new("truncate_to_db_limit #{attribute_name}: No such attribute")
end
limit = ar_attr.limit
unless limit && limit.to_i != 0
raise ArgumentError.new("truncate_to_db_limit #{attribute_name}: Limit not known")
end
define_method "#{attribute_name}=" do |val|
normalized = val.slice(0, limit)
super(normalized)
end
end
end
end