2016-06-09 3 views
1

Вот мои Моделей:единственность Проверка на has_many запись ассоциации через accepts_nested_attributes_for

class User < ApplicationRecord 
    has_many :cities_users, inverse_of: :user 
    has_many :cities, through: :cities_users 

    accepts_nested_attributes_for :cities_users 
end 


class CitiesUser < ApplicationRecord 
    belongs_to :user 
    belongs_to :city 

    validates :user_id, uniqueness: { scope: :city_id, message: "already specified that they have lived in this city"} 
end 


class City < ApplicationRecord 
    has_many :cities_users 
    has_many :users, through: :cities_users 
end 

При создании нового user записи: Пользователь может динамически добавлять города, что они жили в (через nested_form_fields драгоценного камня). Это в конечном итоге создает записи в таблице соединений cities_users.

clicking button

После нажатия кнопки 'Добавить город вы жили в' кнопки:

enter image description here

Пользователь продолжающую и нажимает на кнопку еще раз, чтобы добавить другой город:

click button again

Эта ситуация выше, где я хочу, чтобы ошибка проверки срабатывала, когда пользователь нажимает кнопку Create User. При создании новой записи пользователя: пользователь НЕ должен указывать, что они проживали в одном городе более одного раза.

Так выше: без проверки: дваcities_users записи будет создан с тем же user_id и city_id. Это нехорошо, поэтому я хочу повторно отобразить форму, выделить два оскорбительных nested_fields и сообщить об ошибке: «Вы не можете указать, что пользователь жил в одном городе более одного раза».

Очевидно, для этого требуется подтверждение либо на модели user, либо на модели cities_user. Я не знаю, где должна проходить проверка, и я не знаю, как закодировать проверку, чтобы она ловила эту ошибку. Текущая проверка uniqueness, которую я имею на модели CitiesUser, не улавливает эту ситуацию.

Дополнительная информация настройки только в случае, если кто-то хочет, чтобы воссоздать сценарий

Это было, как я настроить nested_fields внутри пользователя _form.html.erb:

<%= f.nested_fields_for :cities_users do |ff| %> 

<div> 
    <%= ff.label :city_id %> 
    <%= ff.collection_select(:city_id, City.all, :id, :name) %> 
</div> 

<% end %> 

<br> 

<div><%= f.add_nested_fields_link :cities_users, "Add a City You have lived in" %></div> 

users_controller#create действия стандарта, генерируемый из scaffold. Я добавить к сильным Params для того, чтобы для вложенных атрибутов:

def user_params 
    params.require(:user).permit(:name, cities_users_attributes: [:id, :city_id, :user_id]) 
end 

ответ

1

я, наконец, получил это, чтобы сделать работу:

#models/user.rb 
class User < ActiveRecord::Base 

    has_many :cities_users 
    has_many :cities, through: :cities_users 

    accepts_nested_attributes_for :cities_users, allow_destroy: true 

    validate :no_duplicate_cities 

    private 

    def no_duplicate_cities 
    errors.add(:duplicate_cities, "are present") if self.cities_users.group_by(&:city_id).values.detect{|arr| arr.size > 1} 
    end 
end 
1

Самое лучшее, что делать, на мой взгляд, так как вы передаете это к модели пользователя в вашем методе user_params является добавление пользовательской проверки в вашей модели пользователя. Измените идентификатор города, который они добавляют, чтобы быть уникальным в массиве городов, отправленных через «id». Сделайте что-то вроде:

class User < ActiveRecord::Base 

    validate :no_duplicate_cities 

    #I'm assuming user_cities is an array of city id's so we only want one id of each different city at most. 
    def no_duplicate_cities 
    self.user_cities = self.user_cities.uniq 
    end 

Это оставит уникальные идентификаторы в этом массиве, удалив все повторяющиеся города.

+0

спасибо за ваш ответ. этот ответ близок, но как не работает. 'self.users_cities' возвращает объект прокси-объекта коллекции. Что я сделал, так это то, что я создал частный метод 'no_duplicate_cities', поставил там точку останова, называемую' self.users_cities', чтобы посмотреть, что он вернет мне. Вот пример: '# , # , # ]> ' – Neil

+0

возможно как-то мне нужно перебирать элементы в файле collection_proxy см., если элемент с этим 'city_id' уже существует в списке, и если он его отклоняет? – Neil

+0

У меня были надежды, что это будет сделано, но это не так. какие-либо предложения? 'self.users_cities = self.users_cities.uniq {| user_city | user_city.city_id} ' – Neil

0

Я сделал что-то подобное в своем приложении, попробуйте приведенный ниже код в вашей модели пользователя.

validate :unique_cities 

def unique_cities 
    unless self.user_cities.map(&:city_id).uniq 
    errors.add(:cites, "- can not be 2 of the same cities") 
    end 
end 
+0

Спасибо за ответ, Дэйв. К сожалению, метод 'uniq' не делает того, что мы хотели бы здесь. – Neil

+0

ладно, позвольте мне поднять его в пробном свете. – Dave

+0

О, я вижу ваш ответ! хорошая работа, похоже, что ты на правильном пути – Dave

Смежные вопросы