W pełni niestandardowy komunikat o błędzie sprawdzania poprawności za pomocą Rails


260

Używając Railsów, próbuję otrzymać komunikat o błędzie, taki jak „Pole utworu nie może być puste” podczas zapisywania. Wykonując następujące czynności:

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

... wyświetla tylko „Song Rep XYW nie może być pusty”, co nie jest dobre, ponieważ tytuł pola nie jest przyjazny dla użytkownika. Jak mogę zmienić tytuł samego pola? Mógłbym zmienić rzeczywistą nazwę pola w bazie danych, ale mam wiele pól „utworu” i muszę mieć określone nazwy pól.

Nie chcę hakować procesu sprawdzania poprawności szyn i uważam, że powinien istnieć sposób, aby to naprawić.

Odpowiedzi:


432

Teraz akceptowanym sposobem ustawienia humanizowanych nazw i niestandardowych komunikatów o błędach jest użycie ustawień narodowych .

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

Teraz humanizowana nazwa i komunikat potwierdzający obecność atrybutu „email” zostały zmienione.

Komunikaty sprawdzania poprawności można ustawić dla określonego modelu + atrybutu, modelu, atrybutu lub globalnie.


19
Jeśli używasz mongoid, zamień activerecord: na mongoid:
Intentss

88
@graywh: Gdzie należy umieścić pytania dotyczące odpowiedzi, jeśli nie w komentarzach? Oto przewodnik I18n: guide.rubyonrails.org/i18n.html
Tyler Rick

4
Nawiasem mówiąc: jeśli przekażesz symbol parametru wiadomości walidatora w Rails 3.1.3, powie ci on zakres, którego szukał, ponieważ go nie znaleziono, więc dokładnie wiesz, co umieścić w swoim locales yml.
aceofspades

4
Cóż, to w porządku i wszystko, ale co jeśli naiwne poprzedzenie nazwy kolumny (bez względu na to, jak czytelna dla człowieka) doprowadzi do kompletnej gramatyki (szczególnie w językach innych niż angielski)? Czy naprawdę muszę używać errors.add :base, msg? Chciałbym wiedzieć, o której kolumnie chodzi błąd, więc mogę wyświetlić go w odpowiednim polu formularza.
panzi

6
@graywh Może coś mi brakuje, ale czy nie zawsze poprzedza nazwę kolumny przed wiadomością? Nawet po angielsku chciałbym mieć np. The password is wrong.Lub The email address is not valid.zamiast Password is wrong.i Email is not valid..
panzi 10.03.13

65

W twoim modelu:

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

Twoim zdaniem

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

Jeśli to zrobisz

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

pojawia się ten komunikat o błędzie z nazwą atrybutu

address1 Put some address please

jeśli chcesz otrzymać komunikat o błędzie dla jednego atrybutu

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

To nie do przyjęcia rozwiązanie. Co jeśli chcę domyślne zachowanie dla wszystkich innych atrybutów (attr + msg)?
Rômulo Ceccon,

Proszę bardzo. Możesz bawić się tymi 2 rzeczami i sprawić, by działało
Federico

Musisz użyć symbolu, aby wyszukał twoje pliki yml, na przykładvalidates_presence_of :address1, :message => :put_some_address_please
Federico

Jest to nie do przyjęcia, ponieważ nazwa pola zostaje uwzględniona
fatuhoku,

62

Spróbuj tego.

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

Znalazłem to tutaj .

Oto inny sposób, aby to zrobić. To, co robisz, to zdefiniowanie metody human_attribute_name w klasie modelu. Metoda przekazuje nazwę kolumny jako ciąg znaków i zwraca ciąg znaków do użycia w komunikatach sprawdzania poprawności.

class User < ActiveRecord::Base

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

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

end

Powyższy kod pochodzi stąd


Problem polega na tym, że moje pole nazywa się: song_rep_xyz (cóż, coś skomplikowanego), co nie jest przyjazne dla użytkownika
marcgg 30.04.2009

16
w przypadku Rails 3 „def self.human_attribute_name (attr)” należy zmienić na „def self.human_attribute_name (attr, options = {})”, w przeciwnym razie zwraca błąd
klawisz spacemon

3
Dzięki za to. Potrzebowałem czegoś, co zadziałałoby dla Rails 2. (Tak, biedny ja ... :)
Dan Barron

18

Tak, jest na to sposób bez wtyczki! Ale nie jest tak czysty i elegancki jak przy użyciu wspomnianej wtyczki. Oto jest.

Zakładając, że jest to Rails 3 (nie wiem, czy różni się w poprzednich wersjach),

zachowaj to w swoim modelu:

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

i w widoku, zamiast wychodzić

@instance.errors.full_messages

tak jak byłoby w przypadku generatora rusztowań, wstaw:

@instance.errors.first[1]

Otrzymasz tylko wiadomość określoną w modelu, bez nazwy atrybutu.

Wyjaśnienie:

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

Do tej pory wyświetlamy tylko jeden komunikat, zawsze dla pierwszego błędu. Jeśli chcesz wyświetlić wszystkie błędy, możesz zapętlić skrót i wyświetlić wartości.

Mam nadzieję, że to pomogło.


16

Kod Rails3 z całkowicie zlokalizowanymi wiadomościami:

W modelu user.rb zdefiniuj walidację

validates :email, :presence => true

W config / locales / en.yml

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

15

W niestandardowej metodzie weryfikacji użyj:

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

ponieważ add_to_base zostało wycofane.

errors.add_to_base("Custom error message")


13

Związane z zaakceptowaną odpowiedzią i inną odpowiedzią na liście :

Potwierdzam, że rozwidlenie nanamkim custom-err-msg działa z Railsami 5 i ustawieniami regionalnymi.

Musisz tylko uruchomić wiadomość regionalną z karetką i nie powinna ona wyświetlać nazwy atrybutu w wiadomości.

Model zdefiniowany jako:

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

z następującymi en.yml:

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

item.errors.full_messages wyświetli się:

You can't create an item without a name

zamiast zwykłego Name You can't create an item without a name


11

Zalecam zainstalowanie klejnotu custom_error_message (lub wtyczki ) pierwotnie napisanego przez Davida Easleya

Pozwala ci robić takie rzeczy jak:

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

Korzystałem z tej wtyczki w przeszłości z dużym powodzeniem, choć nie wydaje się, aby była regularnie konserwowana.
Jared Brown

1
możesz też zainstalować go jako klejnot dla szyn 3. po prostu dodaj gem "custom_error_message" do swojego Gemfile - zobacz github, aby uzyskać więcej informacji
Dorian

Dokładnie to, czego potrzebowałem
olleicua

3
@DickieBoy Potwierdzam, że widelec nanamkim ( github.com/nanamkim/custom-err-msg ) działa z Rails 5. Właściwie gra dobrze z przyjętą odpowiedzią. Napiszę to jako osobną odpowiedź.
Rystraum

@Rystraum Przez całe życie nie pamiętam, co się z tym wiąże, ale dziękuję za odpowiedź! Na pewno zapamiętam to na przyszłość.
DickieBoy,

10

Jednym z rozwiązań może być zmiana domyślnego formatu błędu i18n:

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

Domyślnie jest format: %{attribute} %{message}


7

Oto inny sposób:

Jeśli używasz tego szablonu:

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

Możesz napisać własną wiadomość w następujący sposób:

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

W ten sposób, z powodu podkreślenia, pełna wiadomość staje się „My custom message”, ale dodatkowa spacja na początku jest niezauważalna. Jeśli naprawdę nie chcesz tej dodatkowej przestrzeni na początku, po prostu dodaj .lstripmetodę.

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

Metoda String.lstrip pozbywa się dodatkowej przestrzeni utworzonej przez „: _” i pozostawia inne komunikaty o błędach bez zmian.

Lub jeszcze lepiej, użyj pierwszego słowa niestandardowej wiadomości jako klucza:

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

Teraz pełna wiadomość będzie „Moja niestandardowa wiadomość” bez dodatkowego miejsca.

Jeśli chcesz, aby cała wiadomość zaczynała się od wielkiej litery, takiej jak „URL nie może być pusty”, nie można tego zrobić. Zamiast tego spróbuj dodać inne słowo jako klucz:

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

Teraz pełna wiadomość będzie „URL nie może być pusty”


ooo, możesz nawet zrobić errors.add(:_, 'foobar')i dostać „foobar” jako wiadomość
xxjjnn

6

Po prostu zrób to normalnie:

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

Zamiast tego wyświetl to w ten sposób

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

Zwroty

"Email is required."

Metoda lokalizacji jest zdecydowanie „właściwym” sposobem na zrobienie tego, ale jeśli wykonujesz mały, nieglobalny projekt i chcesz po prostu szybko zacząć - jest to zdecydowanie łatwiejsze niż przeskakiwanie plików.

Podoba mi się to ze względu na możliwość umieszczenia nazwy pola w innym miejscu niż początek ciągu:

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

2

Jeśli chcesz umieścić je wszystkie na ładnej liście, ale bez użycia grubej, nie przyjaznej dla człowieka nazwy, możesz to zrobić ...

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

1

Twoim zdaniem

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

Jeśli chcesz przesłonić komunikat o błędzie bez nazwy atrybutu, po prostu wstaw wiadomość z ^ tak:

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

nie działa z szynami 5.1 / ruby ​​2.4? uzyskanie nazwy modelu w tym zakresie
Ben

@Ben Działa dla mnie na Railsach 5.1.2, Ruby 2.4.1p111. Czy możesz podzielić się swoim kodem?
luckyruby 17.07.17

Chyba musiałem szukać dalej, możesz sprawdzić kod i jego odpowiedź stackoverflow.com/q/45128434/102133
Ben

0

Próbowałem śledzić, pracował dla mnie :)

1 praca. 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 %> 

0

Oto mój kod, który może ci się przydać na wypadek, gdybyś go nadal potrzebował: Mój model:

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

Następnie stworzyłem pomocnika do wyświetlania wiadomości:

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

Unikalne podejście, o którym nikt nie wspominał!

Jedynym sposobem, w jaki mogłem uzyskać wszystkie potrzebne dostosowania, było użycie after_validationwywołania zwrotnego, aby umożliwić mi manipulowanie komunikatem o błędzie.

  1. Pozwól, aby komunikat sprawdzania poprawności był tworzony normalnie, nie musisz próbować go zmieniać w pomocniku sprawdzania poprawności.

  2. utwórz after_validationwywołanie zwrotne, które zastąpi ten komunikat sprawdzania poprawności w back-endie, zanim przejdzie do widoku.

  3. W after_validationmetodzie możesz zrobić wszystko, co chcesz z komunikatem sprawdzania poprawności, podobnie jak normalny ciąg! Możesz nawet użyć wartości dynamicznych i wstawić je do komunikatu sprawdzania poprawności.


#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

Metoda after_validation będzie miała znacznie większy zasięg niż wbudowany pomocnik sprawdzania poprawności szyn, więc będziesz mógł uzyskać dostęp do sprawdzanego obiektu, tak jak próbujesz zrobić z object.file_name. Który nie działa w pomocniku sprawdzania poprawności, w którym próbujesz to nazwać.

Uwaga: używamy, ^aby pozbyć się nazwy atrybutu na początku sprawdzania poprawności, ponieważ @Rystraum wskazał na odwołanie do tego klejnotu


0

Odpowiedź graywh jest najlepsza, jeśli w rzeczywistości wyświetla się inaczej w wyświetlaniu nazwy pola. W przypadku dynamicznej nazwy pola (na podstawie innych wyświetlanych pól) zrobiłbym coś takiego

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

metoda full_message w innym przypadku jest używana przez szyny w metodzie full_messages, więc wyśle ​​normalne błędy Rails dla innych przypadków (Rails 3.2 i nowsze)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.