Rails: Можно ли отправить отключенную опцию в качестве допустимого значения параметров?

#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. не рекомендуется делать что-то подобное на вашем контроллере, такая логика должна быть в вашей модели