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


260

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

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

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

我不想修改Rails的验证过程,我觉得应该有一种解决方法。

Answers:


432

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

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

现在,“ email”属性的人性化名称状态确认消息已更改。

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


19
如果您使用的是Mongoid,请将activerecord:替换为Mongoid:
Intentss 2011年

88
@graywh:关于答案的问题应该放在哪里(如果不在评论中)?这是I18n指南:guides.rubyonrails.org/i18n.html
泰勒·里克

4
顺便说一句:如果在Rails 3.1.3中为验证器的message参数传递符号,它将告诉您所寻找的范围,因为找不到该范围,因此您确切知道要放入的内容yml语言环境。
aceofspades 2012年

4
好吧,这一切都很好,但是,如果天真地在列名之前添加(无论其可读性如何)会导致语法完全混乱(尤其是在非英语语言中)?我真的需要使用errors.add :base, msg吗?我想知道错误所在的列,因此可以在正确的表单字段中显示它。
panzi

6
@graywh也许我遗漏了一些东西,但是它不是总是在消息前添加列名吗?即使是英文,我也想用eg The password is wrong.The email address is not valid.代替Password is wrong.and Email is not valid.
panzi

65

在您的模型中:

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] %>

那不是一个可以接受的解决方案。如果我想要所有其他属性(attr + msg)的默认行为怎么办?
罗慕洛Ceccon

在那里,您可以..您可以玩这两件事并使它起作用
Federico

您必须使用符号,以便它将在您的yml文件中查找,例如validates_presence_of :address1, :message => :put_some_address_please
Federico

这是不可接受的,因为包含了字段名称
fatuhoku

62

试试这个。

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

我在这里找到的。

这是另一种方法。您要做的是在模型类上定义一个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

上面的代码是从这里


问题是我的字段称为:song_rep_xyz(嗯,有些复杂),它不是用户友好的
marcgg

16
对于Rails 3,需要将“ def self.human_attribute_name(attr)”更改为“ def self.human_attribute_name(attr,options = {})”,否则它将返回错误
spacemonkey,2010年

3
谢谢你 我需要适用于Rails 2的东西。(是的,可怜的我... :)
Dan Barron 2013年

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]

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

希望能有所帮助。


16

带有完全本地化消息的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"

15

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

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

因为add_to_base已被弃用。

errors.add_to_base("Custom error message")


13

接受的答案列表中的另一个答案相关:

我确认nanamkim的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


11

我建议安装最初由David Easley编写的custom_error_message gem(或作为插件

它可以让您执行以下操作:

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

我过去曾经使用此插件取得了巨大的成功,尽管它似乎不再定期维护。
杰里德·布朗

1
您还可以将其安装为rails 3的gem。只需将其添加gem "custom_error_message" 到Gemfile中- 有关更多详细信息,请参见github
Dorian

正是我需要的
olleicua 2015年

3
@DickieBoy我确认nanamkim的fork(github.com/nanamkim/custom-err-msg)与Rails 5兼容。我将把它写成一个单独的答案。
Rystraum '16

@Rystraum对于我的一生,我不记得周围的用例,但感谢您的答复!我一定会为将来记住它。
DickieBoy

10

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

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

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


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

现在,完整的消息将是“ URL不能为空”


噢,你甚至可以做得到errors.add(:_, 'foobar')'foobar'作为消息
xxjjnn 18-10-12

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."

2

如果您想将它们全部列出在一个不错的列表中,但又不使用粗俗的非人类友好名称,则可以执行此操作...

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

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."
  }

不能在Rails 5.1 / ruby​​ 2.4中使用?在该范围内获取模型名称
Ben

@Ben在Rails 5.1.2,Ruby 2.4.1p111上为我工作。可以分享您的代码吗?
luckyruby

我想我必须看得更远,你可以检查代码,而他的回答有stackoverflow.com/q/45128434/102133

0

我尝试了以下内容,为我工作过:)

1份工作

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

2个job_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 %> 

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

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,因此我们在验证开始时使用摆脱了属性名称


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上的full_message方法是rails在full_messages方法中使用的方法,因此它将给出其他情况下的正常Rails错误(Rails 3.2及更高版本)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.