validator validations validates_presence_of validates validate rails method greater_than custom create ruby-on-rails validation

ruby on rails - validations - Mensajes de error de validación de rieles: mostrar solo un mensaje de error por campo



validate integer ruby (13)

¿Qué tal este @event.errors[:title].first ?

Rails muestra todos los mensajes de error de validación asociados con un campo dado. Si tengo tres validates_XXXXX_of :email , y dejo el campo en blanco, recibo tres mensajes en la lista de errores.

Ejemplo:

validates_presence_of :name validates_presence_of :email validates_presence_of :text validates_length_of :name, :in => 6..30 validates_length_of :email, :in => 4..40 validates_length_of :text, :in => 4..200 validates_format_of :email, :with => /^([^@/s]+)@((?:[-a-z0-9]+/.)+[a-z]{2,})$/i<br/>

<%= error_messages_for :comment %> me da:

7 errors prohibited this comment from being saved There were problems with the following fields: Name can''t be blank Name is too short (minimum is 6 characters) Email can''t be blank Email is too short (minimum is 4 characters) Email is invalid Text can''t be blank Text is too short (minimum is 4 characters)

Es mejor mostrar un mensaje a la vez. ¿Hay alguna manera fácil de solucionar este problema? Parece sencillo tener una condición como: Si encontró un error para :email , deje de validar :email y salte al otro campo.


Agregue un método a la clase ActiveModel :: Errors

module ActiveModel class Errors def full_unique_messages unique_messages = messages.map { |attribute, list_of_messages| [attribute, list_of_messages.first] } unique_messages.map { |attribute_message_pair| full_message *attribute_message_pair } end end end

lib/core_ext/rails/active_model/errors.rb a un archivo, como lib/core_ext/rails/active_model/errors.rb . Cree un archivo config/initializers/core_ext.rb y agregue un require "core_ext/rails/active_model/errors.rb" a él.


Bert más en RailsForum escribió sobre esto hace un tiempo. Escribió el código a continuación y agregué algunos ajustes menores para que se ejecutara en Rails-3.0.0-beta2.

Agregue esto a un archivo llamado app/helpers/errors_helper.rb y simplemente agregue helper "errors" a su controlador.

module ErrorsHelper # see: lib/action_view/helpers/active_model_helper.rb def error_messages_for(*params) options = params.extract_options!.symbolize_keys objects = Array.wrap(options.delete(:object) || params).map do |object| object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model) object = convert_to_model(object) if object.class.respond_to?(:model_name) options[:object_name] ||= object.class.model_name.human.downcase end object end objects.compact! count = objects.inject(0) {|sum, object| sum + object.errors.count } unless count.zero? html = {} [:id, :class].each do |key| if options.include?(key) value = options[key] html[key] = value unless value.blank? else html[key] = ''errorExplanation'' end end options[:object_name] ||= params.first I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale| header_message = if options.include?(:header_message) options[:header_message] else locale.t :header, :count => count, :model => options[:object_name].to_s.gsub(''_'', '' '') end message = options.include?(:message) ? options[:message] : locale.t(:body) error_messages = objects.sum do |object| object.errors.on(:name) full_flat_messages(object).map do |msg| content_tag(:li, ERB::Util.html_escape(msg)) end end.join.html_safe contents = '''' contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? contents << content_tag(:p, message) unless message.blank? contents << content_tag(:ul, error_messages) content_tag(:div, contents.html_safe, html) end else '''' end end #################### # # added to make the errors display in a single line per field # #################### def full_flat_messages(object) full_messages = [] object.errors.each_key do |attr| msg_part=msg='''' object.errors[attr].each do |message| next unless message if attr == "base" full_messages << message else msg=object.class.human_attribute_name(attr) msg_part+= I18n.t(''activerecord.errors.format.separator'', :default => '' '') + (msg_part=="" ? '''': '' & '' ) + message end end full_messages << "#{msg} #{msg_part}" if msg!="" end full_messages end end


Creo que la forma más fácil es usar la opción allow_bank. Por ejemplo, para evitar mostrar el mensaje de que el nombre es demasiado corto cuando el campo se deja en blanco, puede hacer lo siguiente:

validates_length_of :name, allow_blank:true, :in => 6..30


Escribí un ayudante personalizado

def display_error(field) if @user.errors[field].any? raw @user.errors[field].first+"<br>" end end

y luego lo uso a la vista en el campo de texto como tal

<%= display_error(:password) %>


Imo simplier es:

<% @model.errors.each do |attr, msg| %> <%= "#{attr} #{msg}" if @model.errors[attr].first == msg %> <% end %>


Mi parche mono de ActiveModel::Errors class lib/core_ext/rails/active_model/errors.rb (uso este código para la versión de Ruby on Rails 5.0):

module ActiveModel class Errors # don''t add an attribute''s error message to details # if it already contains at least one message alias_method :old_add, :add def add(attribute, message = :invalid, options = {}) if details[attribute.to_sym].size.zero? old_add(attribute, message, options) end end end end

Cree un archivo config/initializers/core_ext.rb y agregue un requerimiento core_ext/rails/active_model/errors.rb a él.


Mostraría todos los mensajes de error en una línea y en un formato de oración. No desea que el usuario corrija un error y termine teniendo otro error del que no estaba enterado después del envío. Decirles todas las reglas les ahorrará clics. Dicho esto, así es como lo haría:

flash_message_now("error", @album.errors.keys.map { |k| "#{Album.human_attribute_name(k)} #{@album.errors[k].to_sentence}"}.to_sentence )

con flash_message_now definido en ApplicationController (puede agregarlo a un ayudante)

def flash_message_now(type, text) flash.now[type] ||= [] flash.now[type] << text end


O simplemente puede modificar la matriz (con el método ''bang'' delete_at), por lo que todo lo que sigue se queda en los rieles predeterminados, i18n, etc.

<% @article.errors.keys.each { |attr| @article.errors[attr].delete_at(1) } %>

Código de trabajo completo:

<% if @article.errors.any? %> <% @article.errors.keys.each { |attr| @article.errors[attr].delete_at(1) } %> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> <% end %>


Similar a la respuesta de olovwia :

<% @errors.keys.each do |attr| %> <%= "#{attr.capitalize} #{@errors[attr].first}."%> <% end %>"


Utilizo este código para la versión de Ruby on Rails 3.0, que puse en lib/core_ext/rails/active_model/errors.rb :

module ActiveModel class Errors def full_message_per_field messages_per_field = [] handled_attributes = [] each do |attribute, messages| next if handled_attributes.include? attribute messages = Array.wrap(messages) next if messages.empty? if attribute == :base messages_per_field << messages.first else attr_name = attribute.to_s.gsub(''.'', ''_'').humanize attr_name = @base.class.human_attribute_name(attribute, :default => attr_name) options = { :default => "%{attribute} %{message}", :attribute => attr_name } messages_per_field << I18n.t(:"errors.format", options.merge(:message => messages.first)) end handled_attributes << attribute end messages_per_field end end end

Este es esencialmente el mismo código que ActiveModel::Errors#full_messages , pero no mostrará más de un error por atributo. Asegúrese de solicitar el archivo (por ejemplo, en un inicializador) y ahora puede llamar @model.errors.full_message_per_field do |message| ... @model.errors.full_message_per_field do |message| ...


[Actualización] Enero / 2013 a Rails 3.2.x - sintaxis de actualización; agregar especificaciones

Inspirado por los nuevos métodos de validation en Rails 3.0, estoy agregando este pequeño Validator. Lo llamo ReduceValidator .

lib/reduce_validator.rb :

# show only one error message per field # class ReduceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return until record.errors.messages.has_key?(attribute) record.errors[attribute].slice!(-1) until record.errors[attribute].size <= 1 end end

Mi modelo se ve como ... fíjate en :reduce => true :

validates :title, :presence => true, :inclusion => { :in => %w[ Mr Mrs ] }, :reduce => true validates :firstname, :presence => true, :length => { :within => 2..50 }, :format => { :without => /^/D{1}[.]/i }, :reduce => true validates :lastname, :presence => true, :length => { :within => 2..50 }, :format => { :without => /^/D{1}[.]/i }, :reduce => true

Funciona como un encanto en mi proyecto actual de Rails. La ventaja es que puse el validador solo en algunos campos, no en todos.

spec/lib/reduce_validator_spec.rb :

require ''spec_helper'' describe ReduceValidator do let(:reduce_validator) { ReduceValidator.new({ :attributes => {} }) } let(:item) { mock_model("Item") } subject { item } before(:each) do item.errors.add(:name, "message one") item.errors.add(:name, "message two") end it { should have(2).error_on(:name) } it "should reduce error messages" do reduce_validator.validate_each(item, :name, '''') should have(1).error_on(:name) end end


# Extracts at most <strong>one error</strong> message <strong>per field</strong> from the errors-object. # @param [ActiveModel::Errors] the_errors_object The errors-object. # @raise [ArgumentError] If the given argument is not an instance of ActiveModel::Errors. # @return [Array] A string-array containing at most one error message per field from the given errors-object. def get_one_error_per_field(the_errors_object) if the_errors_object.is_a? ActiveModel::Errors errors = {} the_errors_object.each do |field_name, associated_error| errors[field_name] = the_errors_object.full_message(field_name, associated_error) unless errors[field_name] end return errors.values else raise ArgumentError.new(''The given argument isn/'t an instance of ActiveModel::Errors!'') end end