Thông báo lỗi xác thực hoàn toàn tùy chỉnh với Rails


260

Sử dụng Rails Tôi đang cố gắng nhận thông báo lỗi như "Trường bài hát không thể để trống" khi lưu. Làm như sau:

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

... chỉ hiển thị "Bài hát đại diện XYW không được để trống", điều này không tốt vì tiêu đề của trường không thân thiện với người dùng. Làm thế nào tôi có thể thay đổi tiêu đề của chính lĩnh vực này? Tôi có thể thay đổi tên thực tế của trường trong cơ sở dữ liệu, nhưng tôi có nhiều trường "bài hát" và tôi cần phải có tên trường cụ thể.

Tôi không muốn hack xung quanh quá trình xác nhận của đường ray và tôi cảm thấy nên có cách khắc phục điều đó.

Câu trả lời:


432

Bây giờ, cách được chấp nhận để đặt tên nhân bản và thông báo lỗi tùy chỉnh là sử dụng ngôn ngữ .

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

Bây giờ tên được nhân bản hóa thông báo xác thực hiện diện cho thuộc tính "email" đã được thay đổi.

Thông báo xác thực có thể được đặt cho một mô hình + thuộc tính, mô hình, thuộc tính hoặc toàn cầu cụ thể.


19
Nếu bạn đang sử dụng mongoid, hãy thay thế Activerecord: bằng
mongoid

88
@graywh: Câu hỏi về câu trả lời nên được đăng ở đâu, nếu không có trong phần bình luận? Dưới đây là hướng dẫn I18n: guide.rubyonrails.org/i18n.html
Tyler Rick

4
Nhân tiện: nếu bạn chuyển một ký hiệu cho tham số thông báo của trình xác nhận của bạn trong Rails 3.1.3, nó sẽ cho bạn biết phạm vi mà nó đang tìm kiếm vì nó sẽ không tìm thấy, vì vậy bạn biết chính xác những gì cần đặt vào địa phương yml.
aceofspades

4
Chà, điều này tốt và tất cả, nhưng nếu ngây thơ chuẩn bị tên cột (cho dù con người có thể đọc được như thế nào) sẽ dẫn đến ngữ pháp hoàn toàn f-up (đặc biệt là trong các ngôn ngữ không phải tiếng Anh)? Tôi có thực sự cần sử dụng errors.add :base, msg? Tôi muốn biết cột nào bị lỗi, vì vậy tôi có thể hiển thị nó ở trường biểu mẫu chính xác.
panzi

6
@graywh Có lẽ tôi đang thiếu một cái gì đó, nhưng không phải nó luôn luôn đặt trước tên cột trước tin nhắn? Ngay cả bằng tiếng Anh tôi cũng muốn có ví dụ The password is wrong.hoặc The email address is not valid.thay vì Password is wrong.Email is not valid..
panzi

65

Trong mô hình của bạn:

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

Theo quan điểm của bạn

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

Nếu bạn làm thay

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

bạn nhận được thông báo lỗi này với tên thuộc tính

address1 Put some address please

nếu bạn muốn nhận thông báo lỗi cho một thuộc tính

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

Đó không phải là một giải pháp chấp nhận được. Nếu tôi muốn hành vi mặc định cho tất cả các thuộc tính khác (attr + dir) thì sao?
Rômulo Ceccon

Đến đó đi .. bạn có thể chơi với 2 thứ đó và làm cho nó hoạt động
Federico

Bạn phải sử dụng một biểu tượng để nó sẽ tìm kiếm trong các tệp yml của bạn, nhưvalidates_presence_of :address1, :message => :put_some_address_please
Federico

Điều này không được chấp nhận, vì tên trường được bao gồm
fatuhoku

62

Thử cái này.

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

Tôi tìm thấy điều này ở đây .

Đây là một cách khác để làm điều đó. Những gì bạn làm là định nghĩa một phương thức human_attribution_name trên lớp mô hình. Phương thức được truyền tên cột dưới dạng chuỗi và trả về chuỗi để sử dụng trong thông báo xác thực.

class User < ActiveRecord::Base

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

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

end

Đoạn mã trên là từ đây


Vấn đề là lĩnh vực của tôi được gọi là: song_Vp_xyz (tốt, có gì đó phức tạp), không thân thiện với người dùng
marcgg

16
đối với Rails 3, "def self.human_attribution_name (attr)" cần được đổi thành "def self.human_attribution_name (attr, Options = {})", nếu không, nó sẽ trả về lỗi
spacemonkey

3
Cảm ơn vì điều đó. Tôi cần một cái gì đó hoạt động cho Rails 2. (Vâng, tội nghiệp cho tôi ... :)
Dan Barron

18

Vâng, có một cách để làm điều này mà không cần plugin! Nhưng nó không sạch sẽ và thanh lịch như sử dụng plugin đã đề cập. Nó đây rồi

Giả sử đó là Rails 3 (Tôi không biết nó có khác ở các phiên bản trước không),

giữ điều này trong mô hình của bạn:

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

và trong tầm nhìn, thay vì rời đi

@instance.errors.full_messages

vì nó sẽ là khi chúng ta sử dụng máy phát giàn giáo, đặt:

@instance.errors.first[1]

Và bạn sẽ chỉ nhận được thông báo bạn đã chỉ định trong mô hình, không có tên thuộc tính.

Giải trình:

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

Cho đến nay chúng tôi chỉ hiển thị một thông báo, luôn luôn cho lỗi đầu tiên. Nếu bạn muốn hiển thị tất cả các lỗi, bạn có thể lặp trong hàm băm và hiển thị các giá trị.

Hy vọng rằng đã giúp.


16

Mã Rails3 với các thông điệp được bản địa hóa đầy đủ:

Trong mô hình user.rb xác định xác nhận

validates :email, :presence => true

Trong cấu hình / loc / en.yml

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

15

Trong phương thức xác thực tùy chỉnh, sử dụng:

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

như add_to_base đã không được chấp nhận.

errors.add_to_base("Custom error message")


13

Liên quan đến câu trả lời được chấp nhậnmột câu trả lời khác trong danh sách :

Tôi xác nhận rằng ngã ba tùy chỉnh lỗi của nanamkim hoạt động với Rails 5 và với thiết lập ngôn ngữ.

Bạn chỉ cần bắt đầu tin nhắn miền địa phương bằng dấu mũ và nó không nên hiển thị tên thuộc tính trong tin nhắn.

Một mô hình được định nghĩa là:

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

với những điều sau đây en.yml:

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

item.errors.full_messages sẽ hiển thị:

You can't create an item without a name

thay vì thông thường Name You can't create an item without a name


11

Tôi khuyên bạn nên cài đặt đá quý custom_error_message (hoặc dưới dạng plugin ) ban đầu được viết bởi David Easley

Nó cho phép bạn làm những việc như:

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

Tôi đã sử dụng plugin này trong quá khứ rất thành công mặc dù nó dường như không được duy trì thường xuyên nữa.
Jared Brown

1
bạn có thể cài đặt nó làm đá quý cho đường ray 3. chỉ cần thêm gem "custom_error_message" vào Gemfile của bạn - xem github để biết thêm chi tiết
Dorian

Chính xác những gì tôi cần
olleicua

3
@DickieBoy Tôi xác nhận rằng ngã ba của nanamkim ( github.com/nanamkim/custom-err-msg ) hoạt động với Rails 5. Nó thực sự chơi tốt với câu trả lời được chấp nhận. Tôi sẽ viết nó lên như một câu trả lời riêng biệt.
Rystraum

@Rystraum Đối với cuộc sống của tôi, tôi không thể nhớ usecase xung quanh điều này, nhưng cảm ơn vì đã trả lời! Tôi chắc chắn sẽ nhớ nó cho tương lai.
DickieBoy

10

Một giải pháp có thể là thay đổi định dạng lỗi mặc định của i18n:

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

Mặc định là format: %{attribute} %{message}


7

Đây là một cách khác:

Nếu bạn sử dụng mẫu này:

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

Bạn có thể viết tin nhắn tùy chỉnh của riêng bạn như thế này:

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

Bằng cách này, vì gạch dưới, thông báo đầy đủ trở thành "Thông điệp tùy chỉnh của tôi", nhưng không gian thừa ở đầu không được chú ý. Nếu bạn thực sự không muốn có thêm không gian lúc đầu, chỉ cần thêm .lstripphương thức.

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

Phương thức String.lstrip sẽ loại bỏ không gian thừa được tạo bởi ': _' và sẽ không thay đổi bất kỳ thông báo lỗi nào khác.

Hoặc thậm chí tốt hơn, sử dụng từ đầu tiên của tin nhắn tùy chỉnh của bạn làm chìa khóa:

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

Bây giờ, toàn bộ tin nhắn sẽ là "Tin nhắn tùy chỉnh của tôi" không có thêm dung lượng.

Nếu bạn muốn thông báo đầy đủ bắt đầu bằng một từ viết hoa như "URL không thể để trống" thì không thể thực hiện được. Thay vào đó hãy thử thêm một số từ khác làm chìa khóa:

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

Bây giờ, thông báo đầy đủ sẽ là "URL không thể để trống"


ooo, bạn thậm chí có thể làm errors.add(:_, 'foobar')và lấy 'foobar' làm tin nhắn
xxjjnn

6

Chỉ cần làm theo cách thông thường:

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

Nhưng thay vào đó hãy hiển thị nó như thế này

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

Trả về

"Email is required."

Phương pháp bản địa hóa chắc chắn là cách "phù hợp" để thực hiện việc này, nhưng nếu bạn đang thực hiện một dự án nhỏ, không toàn cầu và muốn thực hiện nhanh chóng - điều này chắc chắn dễ dàng hơn so với nhảy tệp.

Tôi thích nó vì khả năng đặt tên trường ở đâu đó ngoài đầu chuỗi:

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

2

Nếu bạn muốn liệt kê tất cả chúng trong một danh sách đẹp nhưng không sử dụng tên thân thiện với con người, bạn có thể làm điều này ...

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

1

Theo quan điểm của bạn

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

Khi bạn muốn ghi đè thông báo lỗi mà không có tên thuộc tính, chỉ cần thêm thông báo bằng ^ như vậy:

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

không hoạt động với đường ray 5.1 / ruby ​​2.4? lấy tên mô hình trong phạm vi đó
Ben

@Ben Hoạt động với tôi trên Rails 5.1.2, Ruby 2.4.1p111. Bạn có thể chia sẻ mã của bạn?
luckyruby

Tôi đoán tôi đã phải tìm kiếm thêm, bạn có thể kiểm tra mã và câu trả lời của anh ấy ở đó stackoverflow.com/q/45128434/102133
Ben

0

Tôi đã cố gắng làm theo, làm việc cho tôi :)

1 công việc.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_controll.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

Đây là mã của tôi có thể hữu ích cho bạn trong trường hợp bạn vẫn cần nó: Mô hình của tôi:

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

Sau đó, tôi đã tạo một trình trợ giúp để hiển thị các thông báo:

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

Một cách tiếp cận độc đáo tôi chưa thấy ai nhắc đến!

Cách duy nhất tôi có thể nhận được tất cả các tùy chỉnh mà tôi muốn là sử dụng một after_validationcuộc gọi lại để cho phép tôi thao tác thông báo lỗi.

  1. Cho phép thông báo xác thực được tạo như bình thường, bạn không cần phải thử và thay đổi nó trong trình trợ giúp xác thực.

  2. tạo một after_validationcuộc gọi lại sẽ thay thế thông báo xác thực đó ở mặt sau trước khi nó được xem.

  3. Trong after_validationphương thức bạn có thể làm bất cứ điều gì bạn muốn với thông báo xác thực, giống như một chuỗi bình thường! Bạn thậm chí có thể sử dụng các giá trị động và chèn chúng vào thông báo xác nhận.


#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

Phương thức after_validation sẽ có phạm vi lớn hơn nhiều so với trình trợ giúp xác thực rails tích hợp, do đó bạn sẽ có thể truy cập vào đối tượng mà bạn đang xác thực như bạn đang cố gắng thực hiện với object.file_name. Mà không hoạt động trong trình trợ giúp xác nhận nơi bạn đang cố gắng gọi nó.

Lưu ý: chúng tôi sử dụng ^để loại bỏ tên thuộc tính khi bắt đầu xác thực khi @Rystraum chỉ ra tham chiếu điều này viên ngọc


0

Câu trả lời của Graywh là tốt nhất nếu nó thực sự khác nhau trong việc hiển thị tên trường. Trong trường hợp tên trường động (dựa trên các trường khác để hiển thị), tôi sẽ làm một cái gì đó như thế này

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

phương thức full_message trên mặt khác là những gì rails sử dụng bên trong phương thức full_messages, do đó nó sẽ đưa ra các lỗi Rails bình thường cho các trường hợp khác (Rails 3.2 trở lên)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.