在我们的 Rails 应用程序中,我们希望防止用户在文本字段中实际输入的字符多于相应数据库字段中可记录的字符数。这似乎比让他/她打字太多然后被告知事后再试一次要友好得多。
换句话说,所有 View 输入文本字段都应具有与相应 ActiveRecord 模型字段maxlength=
相同的属性集。:limit
varchar()
有没有办法让这种情况自动发生?
如果没有,是否有 DRY 习语、辅助函数或元编程技巧可以做到这一点?
在我们的 Rails 应用程序中,我们希望防止用户在文本字段中实际输入的字符多于相应数据库字段中可记录的字符数。这似乎比让他/她打字太多然后被告知事后再试一次要友好得多。
换句话说,所有 View 输入文本字段都应具有与相应 ActiveRecord 模型字段maxlength=
相同的属性集。:limit
varchar()
有没有办法让这种情况自动发生?
如果没有,是否有 DRY 习语、辅助函数或元编程技巧可以做到这一点?
以下是经过测试和工作的:
# config/initializers/text_field_extensions.rb
module ActionView
module Helpers
module Tags
class TextField
alias_method :render_old, :render
def render
prototype = @object.class
unless prototype == NilClass
maxlength = nil
validator = prototype.validators.detect do |v|
v.instance_of?(ActiveModel::Validations::LengthValidator) &&
v.attributes.include?(@method_name.to_sym) &&
v.options.include?(:maximum)
end
if !validator.nil?
maxlength = validator.options[:maximum]
else
column = prototype.columns.detect do |c|
c.name == @method_name &&
c.respond_to?(:limit)
end
maxlength = column.limit unless column.nil?
end
@options.reverse_merge!(maxlength: maxlength) unless maxlength.nil?
end
render_old
end
end
end
end
end
该解决方案借鉴了Deefour 的答案,但将补丁应用于 ActionView 的TextField
类。所有其他基于文本的输入类型(password_field
、email_field
、search_field
等)都使用从 继承的标签TextField
,这意味着此修复也将适用于它们。唯一的例外是该text_area
方法,它使用不同的标记,除非此补丁单独应用于ActionView::Helpers::Tags::TextArea
.
Deefour 的解决方案查看 ActiveRecord 数据库列来确定最大长度。虽然这完美地回答了 Gene 的问题,但我发现数据库列的限制通常与我们实际想要的字段限制无关(即高于)。最理想的最大长度通常来自LengthValidator
我们在模型上设置的。因此,首先,此补丁LengthValidator
在模型上查找 (a) 应用于该字段的属性,并且 (b) 提供最大长度。如果它没有从中得到任何东西,它将使用数据库列上指定的限制。
最后,就像 Deefour 的回答一样,使用 of@options.reverse_merge!
意味着可以通过在参数中maxlength
指定 a 来覆盖该字段的属性::maxlength
options
<%= form_for @user do |f| %>
<%= f.text_field :first_name, :maxlength => 25 %>
<% end %>
设置:maxlength
为nil
将完全删除该属性。
最后,请记住,提供maxlength
a 选项TextField
会自动将size
输入元素的属性设置为相同的值。就个人而言,我认为该size
属性已经过时并且被 CSS 淘汰了,所以我选择删除它。为此,请将涉及@options.reverse_merge!
以下内容的行替换为:
@options.reverse_merge!(maxlength: maxlength, size: nil) unless maxlength.nil?
类似以下(未经测试)的猴子补丁可能会有所帮助
module ActionView
module Helpers
old_text_field = instance_method(:text_field) # store a reference to the original text_field method
define_method(:text_field) do |object_name, method, options = {}| # completely redefine the text_field method
klass = InstanceTag.new(object_name, method, self, options.delete(:object)).retrieve_object.class # get the class for the object bound to the field
column = klass.columns.reject { |c| c.name != method.to_s }.first if klass.present? # get the column from the class
maxlength = column.limit if defined?(column) && column.respond_to?(:limit) # set the maxlength to the limit for the column if it exists
options.reverse_merge!( maxlength: maxlength ) if defined?(maxlength) # merge the maxlength option in with the rest
old_text_field.bind(self).(object_name, method, options) # pass it up to the original text_field method
end
end
end