#ruby-on-rails
Вопрос:
У меня есть Пользователь и модели стран, и в модели пользователя у него есть отношение has_many :страны. Страна имеет 3 атрибута -> :идентификатор, :имя, :is_main
Предположим, что таблица стран в настоящее время заполнена следующим образом:
id name is_main
----------------------------------
1 USA true
2 Germany nil
3 France nil
4 England nil
Допустим, создан пользователь, у которого есть страны США и Германия. Таким образом, в этом случае user.countries.pluck(:id) вернет [1,2].
Чего я хотел бы добиться, так это того, чтобы при редактировании стран пользователя появлялось выпадающее меню, и мне разрешалось добавлять (или удалять) только те страны, где атрибут «is_main» равен нулю. Другими словами, в раскрывающемся списке страна США должна быть либо отключена, либо полностью скрыта для выбора. В то же время США должны оставаться в списке стран пользователя после отправки формы, а также любые новые страны, которые были добавлены в результате действия обновления.
Короче говоря: Оригинал => user.countries.pluck(:идентификатор) =>> [1,2]
В форме редактирования, если я добавлю Францию пользователю, конечным результатом должно быть user.countries.pluck(:id) => [1,2,3]
Я попробовал следующее:
f.collection_select(:countries, Country.where(is_main: nil), :id, :name, {}, {:multiple => true})
При этом в раскрывающемся списке будут показаны все страны, которые я могу добавить, за исключением США, чего я и хотел. Но проблема в том, что когда я нажимаю «Отправить», параметры[:идентификаторы стран] будут [2, 3]. В результате после действия по обновлению user.countries.pluck(:id) станет [2,3] вместо желаемого [1,2,3], эффективно удаляя идентификатор США.
Есть ли способ обойти это? Я также попытался добавить :отключена опция в collection_select, которая отключает опцию США, но параметры[:идентификаторы стран] все равно будут [ноль, 2, 3]. Буду признателен, если кто-нибудь сможет дать мне совет по этому поводу.
Ответ №1:
Я столкнулся с этой проблемой в своем заявлении. В rails 4 установите include_hidden
атрибут в false
значение в нескольких выбранных тегах selet_tags. Вот как я это сделал.
= f.input :countries, as: :select, collection: Country.where(is_main: nil).map{|country| [country.id, country.name]}, input_html: { multiple: true, data: { placeholder: "Countries"} }, include_hidden: false
Надеюсь, это поможет вам решить вашу проблему
Комментарии:
1. Спасибо за ваши ответы, к сожалению, это не решает проблему. Я хотел бы добиться того, чтобы при отправке формы редактирования параметры[:идентификаторы стран] должны содержать [1,2,3], которые включают идентификатор страны США вместо [2,3] на основе примера сценария в моем посте.
Ответ №2:
На самом деле, используя ваш пример для множественного выбора, ваши параметры после отправки должны иметь параметры[:идентификаторы стран] = [2,3]. Если вы не даете возможность выбрать США, то она просто не будет включена.
Вы можете отключить параметры, делая это:
// Your select id should be 'user_country_ids', if not change it
// This assumes that your first item will always be USA
$('#user_country_ids option:eq(0)').prop('disabled', true);
В любом случае, этот JS можно обойти, поэтому вам также нужно будет обработать это на своем бэкэнде, добавив проверку для этого в свою пользовательскую модель:
class User
has_many :countries
validate :main_countries_always_associateds
private
def main_countries_always_associateds
errors.add(:countries, "must include main ones") if Country.where(is_main: true).any?{ |c| !self.countries.include? c }
end
end
Редактировать:
Если вы хотите, чтобы в каждой стране пользователя всегда были основные страны, вы можете использовать before_validation
обратный вызов для переопределения этого выбора. Я рекомендую также включить отключенные параметры для множественного выбора, чтобы пользователь знал об этом, проверка может оказаться ненужной.
class User
has_many :countries
before_validation :associate_main_countries
# You should use something like this if you want your dropdown to always show marked main countries
def self.new_custom_user
User.new(countries: Country.where(is_main: true))
end
private
def associate_main_countries
Country.where(is_main: true).each do |c|
self.countries << c if !self.countries.include?(c)
end
end
end
Комментарии:
1. Спасибо за ваш ответ. Дело в том, что если я осуществлю такую проверку, то код завершится ошибкой в тот момент, когда код выполнит действие контроллера обновления пользователя. Основная цель состоит в том, чтобы США (или другие страны is_main = true) оставались после того, как я добавлю новые страны пользователю. Я не уверен, есть ли способ скрыть значение параметра USA из выпадающего списка (чтобы предотвратить его выбор), но все равно иметь возможность анализировать параметры во время действия обновления.
2. Еще раз спасибо вам. Если возможно, я предпочитаю не проводить проверку модели, так как возможно, что у пользователя может не быть страны is_main=true (очень редкий случай, но все же может произойти) или у пользователя может быть несколько стран is_main=true. Просто мой пример-это обычный сценарий, с которым я столкнусь. В идеале было бы здорово, если бы был способ проанализировать идентификаторы is_main=true стран в парах вместе с другими добавленными странами после заполнения формы редактирования.
3. Это на вас, если вам не нужна проверка, просто удалите ее. Кроме того, если у вас есть случаи, когда пользователи не должны иметь основных стран, вам нужно будет настроить свой
associate_main_countries
, чтобы разрешить эти случаи4. я согласен, обязательно рассмотрю ваше решение. На данный момент я пытаюсь выяснить, можно ли это сделать прямо во время отправки формы редактирования
5. не рекомендуется делать что-то подобное на вашем контроллере, такая логика должна быть в вашей модели