Rails从ActiveRecord限制输入文本maxlength

时间:2012-09-06 00:55:11

标签: ruby-on-rails activerecord form-helpers

我是我们的Rails应用程序,我们希望阻止用户在文本字段中输入比在相应数据库字段中记录的更多字符。这似乎比让他/她输入太多更友好,然后被告知在事后再试一次。

换句话说,所有视图输入文本字段的maxlength=属性都应设置为等于相应ActiveRecord模型:limit字段的varchar()

有没有办法让这种情况自动发生?

如果没有,是否会出现干扰习惯,辅助功能或元编程黑客攻击?

2 个答案:

答案 0 :(得分:2)

Rails 4解决方案

以下是经过测试和运作的:

# 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's answer,但将修补程序应用于ActionView的TextField类。所有其他基于文本的输入类型(password_fieldemail_fieldsearch_field等)都使用从TextField继承的标记,这意味着此修补程序将适用于它们好。唯一的例外是text_area方法,它使用不同的标记,除非将此修补程序单独应用于ActionView::Helpers::Tags::TextArea,否则将无法以相同的方式运行。

Deefour的解决方案查看ActiveRecord数据库列以确定最大长度。虽然这完全回答了Gene的问题,但我发现数据库列的限制通常与我们实际想要的限制无关(即高于)。最理想的最大长度通常来自我们在模型上设置的LengthValidator。因此,首先,此补丁在模型上查找LengthValidator,(a)应用于用于字段的属性,(b)提供最大长度。如果它没有得到任何东西,它将使用数据库列上指定的限制。

最后,就像Deefour的回答一样,使用@options.reverse_merge!意味着可以通过在maxlength参数中指定:maxlength来覆盖该字段的options属性:< / p>

<%= form_for @user do |f| %>
  <%= f.text_field :first_name, :maxlength => 25 %>
<% end %>

:maxlength设置为nil将完全删除该属性。

最后,请注意,提供maxlength的{​​{1}}选项会自动将input元素的TextField属性设置为相同的值。就个人而言,我认为size属性已过时并且已被CSS淘汰,因此我选择将其删除。为此,请使用以下内容替换涉及size的行:

@options.reverse_merge!

答案 1 :(得分:1)

类似以下(未经测试的)猴子补丁可能会有所帮助

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