簡體   English   中英

使用 Rails 的完全自定義驗證錯誤消息

[英]Fully custom validation error message with Rails

使用 Rails 我試圖在保存時收到一條錯誤消息,如“歌曲字段不能為空”。 執行以下操作:

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

... 只顯示“Song Rep XYW can't be empty”,這不好,因為該字段的標題不是用戶友好的。 如何更改字段本身的標題? 我可以更改數據庫中字段的實際名稱,但我有多個“歌曲”字段,我確實需要有特定的字段名稱。

我不想繞過 Rails 的驗證過程,我覺得應該有辦法解決這個問題。

現在,設置人性化名稱和自定義錯誤消息的公認方法是使用 locales

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

現在,“email”屬性的人性化名稱存在驗證消息已更改。

可以為特定模型+屬性、模型、屬性或全局設置驗證消息。

在您的模型中:

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

嘗試這個。

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

我在這里找到了這個。

Rails 3 到 6 的更新:

validate do |user|
  user.errors.add(:base, "Country can't be blank") if user.country_iso.blank?
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

上面的代碼來自這里

是的,有一種方法可以在沒有插件的情況下做到這一點! 但它不像使用提到的插件那樣干凈和優雅。 這里是。

假設是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]

到目前為止,我們只顯示一條消息,總是針對第一個錯誤。 如果您想顯示所有錯誤,您可以在哈希中循環並顯示值。

希望有所幫助。

在自定義驗證方法中使用:

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

因為 add_to_base 已被棄用。

errors.add_to_base("Custom error message")

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

接受的答案列表中的另一個答案相關:

我確認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

我建議安裝最初由 David Easley 編寫的custom_error_message gem (或作為插件

它可以讓你做這樣的事情:

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

一種解決方案可能是更改 i18n 默認錯誤格式:

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

默認為format: %{attribute} %{message}

這是另一種方式:

如果您使用此模板:

<% 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 不能為空”

只需按照正常方式進行操作:

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

這是我的代碼,如果您仍然需要它,它可能對您有用:我的模型:

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

如果您想將它們全部列在一個不錯的列表中,但又不使用粗俗的非人類友好名稱,您可以這樣做...

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

在你看來

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

我試過以下,為我工作:)

1 份工作.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 %> 

我從未見過任何人提及的獨特方法!

我能夠獲得我想要的所有自定義的唯一方法是使用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

如果在顯示字段名稱時實際區域設置不同,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 3.2 及更高版本)給出正常的 Rails 錯誤

升級后的@Federico 答案對所有字段錯誤都是通用的。

在您的controller.rb

flash[:alert] = @model.errors.messages.values 
# [["field1_err1", "field1_err2"], ["field2_err1"], ["field3_err1"]]

messages方法“返回 Hash 屬性及其錯誤消息數組”,如rails docs中所示。

然后,以表格形式顯示這些錯誤:

<% flash.each do |type, type_arr| %>
  <% type_arr.each do |msg| %>
    <ul>
      <li>
        <%= msg.to_sentence %>
      </li>
    </ul>
  <% end %>
<% end %>

如果您正在使用Rails-5您還可以使用以下內容:

validates:user_name,presence:true,uniqueness:{message:“用戶名已存在。”}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM