#ruby-on-rails-3 #nested-attributes #custom-validators
#ruby-on-rails-3 #вложенные атрибуты #пользовательские валидаторы
Вопрос:
У меня есть следующие модели:
class Evaluation < ActiveRecord::Base
attr_accessible :product_id, :description, :evaluation_institutions_attributes
has_many :evaluation_institutions, :dependent => :destroy
accepts_nested_attributes_for :evaluation_institutions, :reject_if => lambda { |a| a[:token].blank? }, :allow_destroy => true
validate :requires_at_least_one_institution
private
def requires_at_least_one_institution
if evaluation_institution_ids.nil? || evaluation_institution_ids.length == 0
errors.add_to_base("Please select at least one institution")
end
end
end
class EvaluationInstitution < ActiveRecord::Base
attr_accessible :evaluation_institution_departments_attributes, :institution_id
belongs_to :evaluation
has_many :evaluation_institution_departments, :dependent => :destroy
accepts_nested_attributes_for :evaluation_institution_departments, :reject_if => lambda { |a| a[:department_id].blank? }, :allow_destroy => true
validate :requires_at_least_one_department
private
def requires_at_least_one_department
if evaluation_institution_departments.nil? || evaluation_institution_departments.length == 0
errors.add_to_base("Please select at least one department")
end
end
end
class EvaluationInstitutionDepartment < ActiveRecord::Base
belongs_to :evaluation_institution
belongs_to :department
end
У меня есть форма для оценки, которая включает вложенные атрибуты для EvaluationInstitution и EvaluationInstitutionDepartment, поэтому моя форма вложена в 3 уровня. 3-й уровень вызывает у меня проблему.
Ошибки срабатывают, как и ожидалось, но когда ошибка срабатывает для require_sat_least_one_department, текст гласит
База оценочных учреждений, пожалуйста, выберите хотя бы один отдел
Сообщение должно гласить «Пожалуйста, выберите хотя бы один отдел».
Как мне удалить «Базу оценочных учреждений»?
Комментарии:
1. Вы когда-нибудь находили ответ на этот @Kevin? У меня такая же проблема. Интересно, может ли помочь упрощение вопроса ..?
2. Так и не нашел ответа на это.
3. @paul, кстати, моя работа заключалась в том, чтобы перенести всю проверку на модель более высокого уровня, evaluation. Так, например, моя функция :requires_at_least_one_department перемещается в модель оценки. Не самое лучшее решение.
Ответ №1:
В Rails 3.2, если вы посмотрите на реализацию метода full_message, вы увидите, что он отображает сообщения об ошибках через I18n в формате «%{attribute} %{message}».
Это означает, что вы можете настроить отображаемый формат в ваших локализациях I18n следующим образом:
activerecord:
attributes:
evaluation_institutions:
base: ''
Это позволило бы избавиться от префикса «База оценочных учреждений», как вы хотели.
Комментарии:
1. Я не знаю, почему я поддержал это. Я не могу заставить это работать. Это выглядело как достойное решение.
2. Я заставил свой работать, удалив 2-ю строку в этом файле locales … другими словами, не включайте «ошибки» в файл yaml.
3. Для меня мне пришлось использовать имя модели в локальной в единственном числе, а не во множественном числе, т.е.: evaluation_institution
Ответ №2:
Если кто-то ищет решение для этого, которое не требует исправления обезьяной, вот что я сделал в разделе «Мои ошибки частично». Я просто ищу «base» в имени атрибута с ошибкой, и если он существует, я только отправляю сообщение, в противном случае я создаю full_message. Теперь это не сработает, если у вас есть атрибуты, в названии которых есть base, но у меня их нет, поэтому у меня это работает. Это немного халтурно, но таковы и другие решения этой проблемы.
<% if object.errors.any? %>
<div id="error-explanation">
<div class="alert alert-error">
<ul>
<% object.errors.each do |atr, msg| %>
<li>
<% if atr.to_s.include? "base" %>
<%= msg %>
<% else %>
<%= object.errors.full_message(atr, msg) %>
<% end %>
</li>
<% end %>
</ul>
</div>
</div>
<% end %>
Комментарии:
1. Спасибо. Я сделал что-то очень похожее (Rails 4), но с этой менее сложной проверкой:
if atr == :base || atr.to_s.ends_with?(".base")
Ответ №3:
Добавление следующего исправления monkey к инициализаторам выполнило работу за меня в 3.2.3 с помощью dynamic_form:
class ActiveModel::Errors
#exact copy of dynamic_form full_messages except 'attr_name = attr_name.sub(' base', ':')'
def full_messages
full_messages = []
each do |attribute, messages|
messages = Array.wrap(messages)
next if messages.empty?
if attribute == :base
messages.each {|m| full_messages << m }
else
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
attr_name = attr_name.sub(' base', ':')
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
if m =~ /^^/
options[:default] = "%{message}"
full_messages << I18n.t(:"errors.dynamic_format", options.merge(:message => m[1..-1]))
elsif m.is_a? Proc
options[:default] = "%{message}"
full_messages << I18n.t(:"errors.dynamic_format", options.merge(:message => m.call(@base)))
else
full_messages << I18n.t(:"errors.format", options.merge(:message => m))
end
end
end
end
full_messages
end
end
Если вы не используете dynamic_form, попробуйте вместо этого следующее (если только ваш gem формы не переопределяет errors.full_messages, как это делает dynamic_form):
class ActiveModel::Errors
#exact copy of Rails 3.2.3 full_message except 'attr_name = attr_name.sub(' base', ':')'
def full_message(attribute, message)
return message if attribute == :base
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
attr_name = attr_name.sub(' base', ':')
I18n.t(:"errors.format", {
:default => "%{attribute} %{message}",
:attribute => attr_name,
:message => message
})
end
end
Единственным изменением в исходном коде является следующая строка:
attr_name = attr_name.sub(' base', ':')
Предложения приветствуются.
Ответ №4:
Это старый вопрос, но эта проблема снова затронула меня в Rails 6, поэтому публикую свое решение здесь, поскольку это наиболее актуальный пост SO, в котором рассматривается проблема.
Пример: Сохранение класса верхнего уровня: ‘Parent’, содержащего коллекцию ‘Child’, где ‘Child’ имеет пользовательский метод проверки:
например
class Parent < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children, allow_destroy: true, reject_if: :all_blank
end
class Child < ActiveRecord::Base
belongs_to :parent
validate :custom_method
def custom_method
errors.add(:base, :error_symbol)
end
end
Необходимо следующее:
- Предоставление записи локали для ‘error_symbol’
- Предотвращение ошибки для дочерних элементов, отображаемых как «Дочерняя база …»
Решение
Сначала добавьте config.active_model.i18n_customize_full_message = true
в свой файл application.rb.
Затем следующий файл локали работает, чтобы переопределить оба сообщения и предотвратить добавление «Base» к коллекции.
# config/locales/en.yml
en:
activerecord:
errors:
models:
parent/children:
format: 'Child: %{message}'
child:
error_symbol: "Error message goes here"
Интересно, что, похоже, здесь есть некоторое взаимодействие с accepts_nested_attributes_for
, поскольку я смог воспроизвести эту проблему только при создании родительского и дочерних элементов с одним объектом params.
Если у вас это не сработает или у вас более сложная проблема, взгляните на ActiveModel/lib/active_model/errors.rb
метод full_message
.
Это должно сказать вам, является ли:
- Рассматриваемый класс правильно подбирает форматирование i18n для этого класса
- Какие ключи он использует для поиска в файлах локали.