Vollständig benutzerdefinierte Validierungsfehlermeldung mit Rails

260

Mit Rails versuche ich beim Speichern eine Fehlermeldung wie "Das Songfeld kann nicht leer sein" zu erhalten. Folgendes tun:

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

... zeigt nur "Song Rep XYW kann nicht leer sein" an, was nicht gut ist, da der Titel des Feldes nicht benutzerfreundlich ist. Wie kann ich den Titel des Feldes selbst ändern? Ich könnte den tatsächlichen Namen des Feldes in der Datenbank ändern, aber ich habe mehrere "Song" -Felder und ich muss bestimmte Feldnamen haben.

Ich möchte mich nicht um den Validierungsprozess von Rails kümmern, und ich bin der Meinung, dass es eine Möglichkeit geben sollte, dies zu beheben.

marcgg
quelle

Antworten:

432

Die akzeptierte Methode zum Festlegen der humanisierten Namen und benutzerdefinierten Fehlermeldungen ist die Verwendung von Gebietsschemas .

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

Jetzt wurden der humanisierte Name und die Anwesenheitsüberprüfungsnachricht für das Attribut "E-Mail" geändert.

Validierungsnachrichten können für ein bestimmtes Modell + Attribut, Modell, Attribut oder global festgelegt werden.

greywh
quelle
19
Wenn Sie Mongoid verwenden, ersetzen Sie Activerecord: durch Mongoid:
Absichten
88
@graywh: Wo sollen Fragen zu einer Antwort gepostet werden, wenn nicht in den Kommentaren? Hier ist der I18n-Leitfaden : guides.rubyonrails.org/i18n.html
Tyler Rick
4
Übrigens: Wenn Sie in Rails 3.1.3 ein Symbol für den Nachrichtenparameter Ihres Validators übergeben, wird Ihnen der gesuchte Bereich angezeigt, da er nicht gefunden wird, sodass Sie genau wissen, was Sie in Ihren Bereich einfügen müssen Gebietsschemas yml.
Aceofspades
4
Nun, das ist in Ordnung und alles, aber was wäre, wenn das naive Voranstellen des Spaltennamens (egal wie lesbar er ist) zu einer vollständig überarbeiteten Grammatik führen würde (insbesondere in nicht-englischen Sprachen)? Muss ich wirklich verwenden errors.add :base, msg? Ich möchte wissen, um welche Spalte es sich bei dem Fehler handelt, damit ich sie im richtigen Formularfeld anzeigen kann.
Panzi
6
@graywh Vielleicht fehlt mir etwas, aber wird der Spaltenname nicht immer vor der Nachricht vorangestellt? Auch auf Englisch hätte ich gerne zB The password is wrong.oder The email address is not valid.statt Password is wrong.und Email is not valid..
Panzi
65

In Ihrem Modell:

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

Aus Ihrer Sicht

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

Wenn Sie stattdessen tun

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

Sie erhalten diese Fehlermeldung mit dem Attributnamen

address1 Put some address please

Wenn Sie die Fehlermeldung für ein einzelnes Attribut erhalten möchten

<%= @model.errors[:address1] %>
Federico
quelle
Das ist keine akzeptable Lösung. Was ist, wenn ich das Standardverhalten für alle anderen Attribute (attr + msg) möchte?
Rômulo Ceccon
Los geht's ... du kannst mit diesen 2 Dingen spielen und es funktionieren lassen
Federico
Sie müssen ein Symbol verwenden, damit es in Ihren yml-Dateien nachschlägt, wievalidates_presence_of :address1, :message => :put_some_address_please
Federico
Dies ist nicht akzeptabel, da der Feldname enthalten ist
Fatuhoku
62

Versuche dies.

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

Ich habe das hier gefunden .

Hier ist eine andere Möglichkeit, dies zu tun. Sie definieren eine human_attribute_name-Methode für die Modellklasse. Die Methode erhält den Spaltennamen als Zeichenfolge und gibt die Zeichenfolge zurück, die in Validierungsnachrichten verwendet werden soll.

class User < ActiveRecord::Base

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

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

end

Der obige Code stammt von hier

Maulin
quelle
Das Problem ist, dass mein Feld heißt: song_rep_xyz (na ja, etwas kompliziertes), was nicht benutzerfreundlich ist
marcgg
16
Für Rails 3 muss "def self.human_attribute_name (attr)" in "def self.human_attribute_name (attr, options = {})" geändert werden, andernfalls wird ein Fehler zurückgegeben
spacemonkey
3
Danke dafür. Ich brauchte etwas, das für Rails 2 funktionierte. (Ja, arm ich ... :)
Dan Barron
18

Ja, es gibt eine Möglichkeit, dies ohne das Plugin zu tun! Aber es ist nicht so sauber und elegant wie mit dem genannten Plugin. Hier ist es.

Angenommen, es ist Rails 3 (ich weiß nicht, ob es in früheren Versionen anders ist),

Behalte dies in deinem Modell:

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

und in der Ansicht, anstatt zu gehen

@instance.errors.full_messages

wie es wäre, wenn wir den Gerüstgenerator verwenden, setzen Sie:

@instance.errors.first[1]

Und Sie erhalten nur die Nachricht, die Sie im Modell angegeben haben, ohne den Attributnamen.

Erläuterung:

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

Bisher wird nur eine Meldung angezeigt, immer für den ersten Fehler. Wenn Sie alle Fehler anzeigen möchten, können Sie den Hash durchlaufen und die Werte anzeigen.

Hoffe das hat geholfen.

Marco Antonio
quelle
16

Rails3-Code mit vollständig lokalisierten Nachrichten:

Definieren Sie im Modell user.rb die Validierung

validates :email, :presence => true

In config / locales / en.yml

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

Verwenden Sie in der benutzerdefinierten Validierungsmethode:

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

da add_to_base veraltet ist.

errors.add_to_base("Custom error message")

amit_saxena
quelle
13

Bezogen auf die akzeptierte Antwort und eine andere Antwort in der Liste :

Ich bestätige, dass Nanamkims Fork von custom-err-msg mit Rails 5 und mit dem Gebietsschema-Setup funktioniert.

Sie müssen die Gebietsschemanachricht nur mit einem Caret beginnen und es sollte nicht der Attributname in der Nachricht angezeigt werden.

Ein Modell definiert als:

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

mit folgendem en.yml:

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

item.errors.full_messages wird angezeigt:

You can't create an item without a name

statt des üblichen Name You can't create an item without a name

Rystraum
quelle
11

Ich empfehle die Installation des Gems custom_error_message (oder als Plugin ), das ursprünglich von David Easley geschrieben wurde

Damit können Sie Dinge tun wie:

validates_presence_of :non_friendly_field_name, :message => "^Friendly field name is blank"
Ryan Bigg
quelle
Ich habe dieses Plugin in der Vergangenheit mit großem Erfolg verwendet, obwohl es nicht mehr regelmäßig gewartet zu werden scheint.
Jared Brown
1
Sie können es auch als Edelstein für Schienen 3 installieren. gem "custom_error_message" Fügen Sie es einfach zu Ihrem Gemfile hinzu - siehe Github für weitere Details
Dorian
Genau das, was ich brauchte
olleicua
3
@ DickieBoy Ich bestätige, dass die Gabel von nanamkim ( github.com/nanamkim/custom-err-msg ) mit Rails 5 funktioniert. Sie spielt tatsächlich gut mit der akzeptierten Antwort. Ich werde dies als separate Antwort aufschreiben.
Rystraum
@Rystraum Für mein Leben kann ich mich nicht an den Anwendungsfall erinnern, aber danke für die Antwort! Ich werde mich sicher für die Zukunft daran erinnern.
DickieBoy
10

Eine Lösung könnte darin bestehen, das Standardfehlerformat von i18n zu ändern:

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

Standard ist format: %{attribute} %{message}

cappie013
quelle
7

Hier ist ein anderer Weg:

Wenn Sie diese Vorlage verwenden:

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

Sie können Ihre eigene benutzerdefinierte Nachricht wie folgt schreiben:

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

Auf diese Weise wird die vollständige Nachricht aufgrund des Unterstrichs zu "Meine benutzerdefinierte Nachricht", aber der zusätzliche Platz am Anfang ist nicht wahrnehmbar. Wenn Sie diesen zusätzlichen Platz am Anfang wirklich nicht möchten, fügen Sie einfach die .lstripMethode hinzu.

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

Die String.lstrip-Methode entfernt den zusätzlichen Speicherplatz, der durch ': _' erstellt wurde, und lässt alle anderen Fehlermeldungen unverändert.

Oder noch besser, verwenden Sie das erste Wort Ihrer benutzerdefinierten Nachricht als Schlüssel:

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

Jetzt lautet die vollständige Nachricht "Meine benutzerdefinierte Nachricht" ohne zusätzlichen Speicherplatz.

Wenn Sie möchten, dass die vollständige Nachricht mit einem Großbuchstaben wie "URL darf nicht leer sein" beginnt, ist dies nicht möglich. Versuchen Sie stattdessen, ein anderes Wort als Schlüssel hinzuzufügen:

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

Jetzt lautet die vollständige Nachricht "Die URL darf nicht leer sein".

Cruz Nunez
quelle
ooo, du kannst sogar errors.add(:_, 'foobar')'foobar' als Nachricht bekommen
xxjjnn
6

Mach es einfach wie gewohnt:

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

Aber zeigen Sie es stattdessen so an

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

Kehrt zurück

"Email is required."

Die Lokalisierungsmethode ist definitiv der "richtige" Weg, dies zu tun. Wenn Sie jedoch ein kleines, nicht globales Projekt durchführen und einfach nur schnell loslegen möchten, ist dies definitiv einfacher als das Hüpfen von Dateien.

Ich mag es für die Möglichkeit, den Feldnamen an einer anderen Stelle als am Anfang der Zeichenfolge zu platzieren:

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

Wenn Sie sie alle in einer schönen Liste auflisten möchten, ohne jedoch den groben, nicht menschlich freundlichen Namen zu verwenden, können Sie dies tun ...

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

Aus Ihrer Sicht

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

Wenn Sie die Fehlermeldung ohne den Attributnamen überschreiben möchten, stellen Sie der Nachricht einfach Folgendes vor:

validates :last_name,
  uniqueness: {
    scope: [:first_name, :course_id, :user_id],
    case_sensitive: false,
    message: "^This student has already been registered."
  }
Glückspilz
quelle
funktioniert nicht mit Schienen 5.1 / Ruby 2.4? Erhalten des Modellnamens in diesem Bereich
Ben
@ Ben Funktioniert für mich auf Rails 5.1.2, Ruby 2.4.1p111. Können Sie Ihren Code teilen?
Glückyruby
Ich denke, ich musste weiter suchen, Sie können den Code und seine Antwort dort überprüfen stackoverflow.com/q/45128434/102133
Ben
0

Ich habe versucht zu folgen, habe für mich gearbeitet :)

1 job.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 %> 
Aigul
quelle
0

Hier ist mein Code, der für Sie nützlich sein kann, falls Sie ihn noch benötigen: Mein Modell:

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

Dann habe ich einen Helfer erstellt, um Nachrichten anzuzeigen:

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
Oleksii Danylevskyi
quelle
0

Ein einzigartiger Ansatz, den noch niemand erwähnt hat!

Die einzige Möglichkeit, alle gewünschten Anpassungen zu erhalten, bestand darin, einen after_validationRückruf zu verwenden, um die Fehlermeldung zu bearbeiten.

  1. Wenn die Validierungsnachricht wie gewohnt erstellt werden soll, müssen Sie nicht versuchen, sie im Validierungshelfer zu ändern.

  2. Erstellen Sie einen after_validationRückruf, der diese Validierungsnachricht im Back-End ersetzt, bevor sie zur Ansicht gelangt.

  3. In der after_validationMethode können Sie mit der Validierungsnachricht alles tun, was Sie wollen, genau wie mit einer normalen Zeichenfolge! Sie können sogar dynamische Werte verwenden und diese in die Validierungsnachricht einfügen.


#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

Die after_validation-Methode hat einen weitaus größeren Umfang als der integrierte Rails-Validierungshelfer, sodass Sie auf das zu validierende Objekt zugreifen können, wie Sie es mit object.file_name versuchen. Was im Validierungs-Helfer, in dem Sie ihn aufrufen möchten, nicht funktioniert.

Hinweis: Wir verwenden das ^, um den Attributnamen zu Beginn der Validierung zu entfernen, da @Rystraum darauf hingewiesen hat, dass auf dieses Juwel verwiesen wird

Sami Birnbaum
quelle
0

Die Antwort von greywh ist die beste, wenn der Feldname tatsächlich anders angezeigt wird. Im Fall eines dynamischen Feldnamens (basierend auf anderen anzuzeigenden Feldern) würde ich so etwas tun

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

Die Methode full_message auf der anderen Seite wird von Rails innerhalb der Methode full_messages verwendet, sodass die normalen Rails-Fehler für andere Fälle ausgegeben werden (Rails 3.2 und höher).

Ein Nguyen Dang
quelle