Повністю спеціальне повідомлення про помилку перевірки з Rails


260

Використовуючи Rails, я намагаюся отримати повідомлення про помилку типу "Поле пісні не може бути порожнім" при збереженні. Виконайте наступне:

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

... відображається лише "Song Rep XYW не може бути порожнім", що не добре, оскільки назва поля не є зручною для користувачів. Як я можу змінити назву самого поля? Я міг би змінити фактичну назву поля в базі даних, але у мене є кілька полів "пісні", і мені потрібно мати конкретні назви полів.

Я не хочу ламати процес перевірки рейок, і я вважаю, що має бути спосіб виправити це.

Відповіді:


432

Тепер прийнятим способом встановлення гуманізованих імен та спеціальних повідомлень про помилки є використання локалів .

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

Тепер гуманізоване ім’я та повідомлення про підтвердження присутності для атрибута "email" було змінено.

Повідомлення про валідацію можна встановити для конкретної моделі + атрибута, моделі, атрибута або глобально.


19
Якщо ви використовуєте монгоїд, замініть activerecord: на mongoid:
Intentss

88
@graywh: Де слід розміщувати питання щодо відповіді, якщо не в коментарях? Ось посібник I18n
Тайлер Рік

4
До речі: якщо ви передасте символ для параметра повідомлення свого валідатора в Rails 3.1.3, він підкаже вам сферу, яку він шукав, оскільки він не знайдеться, тож ви точно знаєте, що поставити у свій locales yml.
ацеосфас

4
Що ж, це добре і все, але що робити, якщо наївне попередження назви стовпця (незалежно від того, наскільки воно є читабельним для людини) призвело б до повністю розробленої граматики (особливо в не англійській мові)? Мені справді потрібно користуватися errors.add :base, msg? Я хотів би знати, в якому стовпці йдеться про помилку, тому я можу відобразити її у правильному полі форми.
панци

6
@graywh Можливо, мені щось не вистачає, але чи не завжди це вказує назву стовпця перед повідомленням? Навіть на англійській мові , я хотів би мати , наприклад , The password is wrong.або The email address is not valid.замість Password is wrong.і Email is not valid..
панци

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)?
Ромуло Цекон

Ось ви й можете .. Ви можете пограти з тими двома речами і змусити його працювати
Федеріко

Ви повинні використовувати символ, щоб він знаходився у ваших файлах 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

Я знайшов це тут .

Ось ще один спосіб зробити це. Що ви робите, це визначити метод людини_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

3
Дякую за це Мені потрібно було щось, що працювало на Rails 2. (Так, бідолаха ... :)
Ден Баррон

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

оскільки додаток_да_бази застаріло.

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

Я рекомендую встановити gem custom_error_message (або як плагін ), оригінально написаний Девідом Іслілі

Це дозволяє робити такі речі, як:

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

Раніше я використовував цей плагін з великим успіхом, хоча він, здається, більше не підтримується регулярно.
Джаред Браун

1
Ви можете встановити його як дорогоцінний камінь для рейок 3. просто додайте gem "custom_error_message" до свого Gemfile - докладнішу інформацію див. у Github
Доріан

Саме те, що мені було потрібно
olleicua

3
@DickieBoy Я підтверджую, що виделка nanamkim ( github.com/nanamkim/custom-err-msg ) працює з Rails 5. Це насправді добре грає з прийнятою відповіддю. Я напишу це як окрему відповідь.
Ристраум

@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 не може бути порожнім"


ooo, ви навіть можете зробити errors.add(:_, 'foobar')і отримати "foobar" як повідомлення
xxjjnn

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

не працює з рейками 5.1 / ruby ​​2.4? отримання назви моделі в цьому масштабі
Бен

@Ben працює для мене на Rails 5.1.2, Ruby 2.4.1p111. Чи можете ви поділитися своїм кодом?
luckyruby

я думаю, мені довелося шукати далі, ви можете перевірити код і його відповідь там stackoverflow.com/q/45128434/102133
Ben

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 вакансії_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 матиме набагато більший обсяг, ніж вбудований помічник перевірки рейок, тому ви зможете отримати доступ до об'єкта, який ви перевіряєте, як ви намагаєтесь зробити з object.file_name. Що не працює у помічнику з перевірки, де ви намагаєтесь його зателефонувати.

Примітка. Ми використовуємо, ^щоб позбутися імені атрибута на початку перевірки, як @Rystraum вказував на посилання на цей дорогоцінний камінь


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

метод full_message на іншому - це те, що рейки використовуються всередині методу full_messages, тому він видасть нормальні помилки Rails для інших випадків (Rails 3.2 і вище)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.