使用Rails完全自定义验证错误消息

时间:2009-04-30 19:05:30

标签: ruby-on-rails

使用Rails我试图在保存时收到类似“歌曲字段不能为空”的错误消息。执行以下操作:

validates_presence_of :song_rep_xyz, :message => "can't be empty"

...仅显示“Song Rep XYW不能为空”,这不好,因为该字段的标题不是用户友好的。如何更改字段本身的标题?我可以更改数据库中字段的实际名称,但我有多个“歌曲”字段,我需要有特定的字段名称。

我不想破解rails的验证过程,我觉得应该有办法解决这个问题。

18 个答案:

答案 0 :(得分:403)

现在,设置人性化名称和自定义错误消息的可接受方式是use locales

# config/locales/en.yml
en:
  activerecord:
    attributes:
      user:
        email: "E-mail address"
    errors:
      models:
        user:
          attributes:
            email:
              blank: "is required"

现在人性化名称​​和“email”属性的状态验证消息已更改。

可以为特定模型+属性,模型,属性或全局设置验证消息。

答案 1 :(得分:63)

在你的模特中:

validates_presence_of :address1, message: 'Put some address please' 

在你看来

<% m.errors.each do |attr, msg|  %>
 <%= msg %>
<% end %>

如果你改为

<%= attr %> <%= msg %>

您收到带有属性名称

的错误消息
address1 Put some address please

如果您想获取单个属性的错误消息

<%= @model.errors[:address1] %>

答案 2 :(得分:59)

试试这个。

class User < ActiveRecord::Base
  validate do |user|
    user.errors.add_to_base("Country can't be blank") if user.country_iso.blank?
  end
end

我发现了here

这是另一种方法。 您要做的是在模型类上定义human_attribute_name方法。该方法作为字符串传递列名,并返回要在验证消息中使用的字符串。

class User < ActiveRecord::Base

  HUMANIZED_ATTRIBUTES = {
    :email => "E-mail address"
  }

  def self.human_attribute_name(attr)
    HUMANIZED_ATTRIBUTES[attr.to_sym] || super
  end

end

以上代码来自here

答案 3 :(得分:18)

是的,没有插件就有办法做到这一点! 但它并不像使用上述插件那样干净优雅。在这里。

假设它是Rails 3(我不知道它在以前的版本中是否有所不同),

将其保留在您的模型中:

validates_presence_of :song_rep_xyz, :message => "can't be empty"

并且在视图中,而不是离开

@instance.errors.full_messages

就像我们使用脚手架发电机时一样,放:

@instance.errors.first[1]

您将获得您在模型中指定的消息,而没有属性名称。

说明:

#returns an hash of messages, one element foreach field error, in this particular case would be just one element in the hash:
@instance.errors  # => {:song_rep_xyz=>"can't be empty"}

#this returns the first element of the hash as an array like [:key,"value"]
@instance.errors.first # => [:song_rep_xyz, "can't be empty"]

#by doing the following, you are telling ruby to take just the second element of that array, which is the message.
@instance.errors.first[1]

到目前为止,我们只显示一条消息,始终是第一个错误。如果您想显示所有错误,可以在哈希中循环并显示值。

希望有所帮助。

答案 4 :(得分:15)

带有完全本地化消息的Rails3代码:

在模型user.rb中定义验证

validates :email, :presence => true

在config / locales / en.yml

en:  
  activerecord:
    models: 
      user: "Customer"
    attributes:
      user:
        email: "Email address"
    errors:
      models:
        user:
          attributes:
            email:
              blank: "cannot be empty"

答案 5 :(得分:14)

在自定义验证方法中使用:

errors.add(:base, "Custom error message")

因为不推荐使用add_to_base。

errors.add_to_base("Custom error message")

答案 6 :(得分:11)

我建议安装最初由David Easley撰写的custom_error_message gem(或plugin

它可以让你做类似的事情:

validates_presence_of :non_friendly_field_name, :message => "^Friendly field name is blank"

答案 7 :(得分:11)

accepted answeranother answer down the list相关:

我确认nanamkim's fork of custom-err-msg适用于Rails 5,并且使用区域设置。

您只需要使用插入符启动语言环境消息,它不应在消息中显示属性名称。

定义为的模型:

class Item < ApplicationRecord
  validates :name, presence: true
end

使用以下en.yml

en:
  activerecord:
    errors:
      models:
        item:
          attributes:
            name:
              blank: "^You can't create an item without a name."

item.errors.full_messages将显示:

You can't create an item without a name

而不是通常的Name You can't create an item without a name

答案 8 :(得分:7)

这是另一种方式:

如果您使用此模板:

<% if @thing.errors.any? %>
  <ul>
    <% @thing.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

您可以编写自己的自定义消息,如下所示:

class Thing < ActiveRecord::Base

  validate :custom_validation_method_with_message

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:_, "My custom message")
    end
  end

这样,由于下划线,完整的消息变为“我的自定义消息”,但开头的额外空间不明显。如果你真的不想在开头增加空间,只需添加.lstrip方法。

<% if @thing.errors.any? %>
  <ul>
    <% @thing.errors.full_messages.each do |message| %>
      <li><%= message.lstrip %></li>
    <% end %>
  </ul>
<% end %>

String.lstrip方法将摆脱':_'创建的额外空间,并保留其他任何错误消息。

或者甚至更好,使用自定义消息的第一个单词作为密钥:

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:my, "custom message")
    end
  end

现在,完整的消息将是“我的自定义消息”,没有多余的空间。

如果您希望完整的消息以大写字母开头,例如“URL不能为空”,则无法完成。而是尝试添加一些其他单词作为键:

  def custom_validation_method_with_message
    if some_model_attribute.blank?
      errors.add(:the, "URL can't be blank")
    end
  end

现在完整的消息将是“网址不能为空”

答案 9 :(得分:7)

一种解决方案可能是更改i18n默认错误格式:

en:
  errors:
    format: "%{message}"

默认为format: %{attribute} %{message}

答案 10 :(得分:6)

按照正常方式行事:

validates_presence_of :email, :message => "Email is required."

但是显示它是这样的

<% if @user.errors.any? %>
  <% @user.errors.messages.each do |message| %>
    <div class="message"><%= message.last.last.html_safe %></div>
  <% end %>
<% end %>

返回

"Email is required."

本地化方法绝对是做到这一点的“正确”方法,但是如果你正在做一点非全局项目并希望快速进行 - 这肯定比文件跳转更容易。

我喜欢将字段名称放在字符串开头以外的地方:

validates_uniqueness_of :email, :message => "There is already an account with that email."

答案 11 :(得分:2)

如果你想将它们全部列在一个不错的列表中,但没有使用这个非常友好的名字,你可以这样做......

object.errors.each do |attr,message|
  puts "<li>"+message+"</li>"
end

答案 12 :(得分:1)

在你看来

object.errors.each do |attr,msg|
  if msg.is_a? String
    if attr == :base
      content_tag :li, msg
    elsif msg[0] == "^"
      content_tag :li, msg[1..-1]
    else
      content_tag :li, "#{object.class.human_attribute_name(attr)} #{msg}"
    end
  end
end

如果要覆盖没有属性名称的错误消息,只需在消息前加上^,如下所示:

validates :last_name,
  uniqueness: {
    scope: [:first_name, :course_id, :user_id],
    case_sensitive: false,
    message: "^This student has already been registered."
  }

答案 13 :(得分:0)

我试过跟随,为我工作:)

1 job.rb

class Job < ApplicationRecord
    validates :description, presence: true
    validates :title, 
              :presence => true, 
              :length => { :minimum => 5, :message => "Must be at least 5 characters"}
end

2 jobs_controller.rb

def create
      @job = Job.create(job_params)
      if @job.valid?
        redirect_to jobs_path
      else
        render new_job_path
      end     
    end

3 _form.html.erb

<%= form_for @job do |f| %>
  <% if @job.errors.any? %>
    <h2>Errors</h2>
    <ul>
      <% @job.errors.full_messages.each do |message|%>
        <li><%= message %></li>
      <% end %>  
    </ul>
  <% end %>
  <div>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>
  <div>
    <%= f.label :description %>
    <%= f.text_area :description, size: '60x6' %>

  </div>
  <div>
    <%= f.submit %>
  </div>
<% end %> 

答案 14 :(得分:0)

以下是我的代码,如果您仍然需要它可以对您有用: 我的模特:

validates :director, acceptance: {message: "^Please confirm that you are a director of the company."}, on: :create, if: :is_director?

然后我创建了一个帮助来显示消息:

module ErrorHelper
  def error_messages!
    return "" unless error_messages?
    messages = resource.errors.full_messages.map { |msg|
       if msg.present? && !msg.index("^").nil?
         content_tag(:p, msg.slice((msg.index("^")+1)..-1))
       else
         content_tag(:p, msg)
       end
    }.join

    html = <<-HTML
      <div class="general-error alert show">
        #{messages}
      </div>
    HTML

    html.html_safe
  end

  def error_messages?
    !resource.errors.empty?
  end
end

答案 15 :(得分:0)

如果您使用的是Rails-5,则还可以执行以下操作:

  

验证:user_name,状态:true,唯一性:{消息:“用户名已存在。”}

答案 16 :(得分:0)

  

一种我从未见过有人提及的独特方法!

我能够获得所有想要的自定义的唯一方法是使用after_validation回调来允许我处理错误消息。

  1. 允许正常创建验证消息,您无需尝试在验证助手中进行更改。

  2. 创建一个after_validation回调,它将在到达视图之前在后端替换该验证消息。

  3. after_validation方法中,您可以对验证消息做任何您想做的事情,就像普通的字符串一样!您甚至可以使用动态值并将其插入到验证消息中。


#this could be any validation
validates_presence_of :song_rep_xyz, :message => "whatever you want - who cares - we will replace you later"

after_validation :replace_validation_message

def replace_validation_message
    custom_value = #any value you would like
    errors.messages[:name_of_the_attribute] = ["^This is the replacement message where 
    you can now add your own dynamic values!!! #{custom_value}"]
end

after_validation方法的作用域将比内置的rails验证帮助器大得多,因此您将能够像使用object.file_name一样访问要验证的对象。在您试图调用它的验证帮助器中,这不起作用。

注意:正如@Rystraum指出引用此gem

一样,我们在验证开始时使用^来摆脱属性名称。

答案 17 :(得分:0)

如果

graywh的答案在显示字段名称时实际上在语言上有所不同,那是最好的。如果是动态字段名称(基于要显示的其他字段),我将执行以下操作

<% object.errors.each do |attr, msg| %>
<li>
  <% case attr.to_sym %>
  <% when :song_rep_xyz %>
    <%= #display error how you want here %>
  <% else %>
    <%= object.errors.full_message(attr, msg) %>
  <% end %>
</li>
<% end %>

else上的其他方法是rails在full_messages方法内部使用的方法,因此它将给出其他情况(Rails 3.2及更高版本)的正常Rails错误