Избегание `save!` на имеет много через ассоциацию

#ruby-on-rails #ruby-on-rails-3 #validation #activerecord #has-many-through

#ruby-on-rails #ruby-on-rails-3 #проверка #activerecord #имеет много сквозных

Вопрос:

У меня есть has_many через ассоциацию с атрибутом и некоторые проверки в «модели объединения». Когда я пытаюсь сделать что-то подобное @user.projects << @project , и ассоциация уже создана (таким образом, проверка уникальности завершается неудачей), вместо ошибки, добавляемой к ошибкам проверки, возникает исключение.

 class User 
  has_many :project_users
  has_many :projects, :through => :project_users

class Project
  has_many :project_users
  has_many :users, :through => :project_users

class ProjectUser
  belongs_to :user
  belongs_to :project


# ...
if @user.projects << @project
  redirect_to 'somewhere'
else
  render :new
end
  

Как я могу создать ассоциацию, как с << методом, но вызывая save вместо save! , чтобы я мог показывать ошибки проверки в своей форме вместо того, чтобы использовать a rescue , чтобы перехватить это и обработать соответствующим образом?

Ответ №1:

Я не думаю, что вы можете. Из API:

коллекция<<(object, …) Добавляет один или несколько объектов в коллекцию, устанавливая их внешние ключи в первичный ключ коллекции. Обратите внимание, что эта операция мгновенно запускает update sql, не дожидаясь вызова save или update для родительского объекта.

и

Если при замене коллекции (через association= ) происходит сбой сохранения, возникает исключение ActiveRecord::RecordNotSaved и назначение отменяется.

Обходной путь может выглядеть следующим образом:

 if @user.projects.exists? @project
  @user.errors.add(:project, "is already assigned to this user") # or something to that effect
  render :new
else 
  @user.projects << @projects
  redirect_to 'somewhere'
end
  

Это позволило бы вам обнаружить сбой там, где ассоциация уже существует. Конечно, если другие проверки ассоциации могут быть неудачными, вам все равно нужно перехватить исключение, так что это может быть не очень полезно.

Комментарии:

1. Хм. Я видел это в документах API, но на самом деле это не относится к тому, что он делает со связанной моделью. Используя :through , он фактически запускает проверки и обратные вызовы, как обычно, в «модели объединения». Бит о возникновении ActiveRecord::RecordNotSaved ошибки не имеет значения, потому что он ссылается на замену / назначение коллекции association = .

Ответ №2:

Может быть, вы могли бы попробовать добавить проверку в свою модель проекта, например:

проверяет: идентификатор пользователя, : уникальность => {: область действия => : идентификатор пользователя}, :on =>:создать

Не уверен, поможет ли это избежать сохранения! метод..

Комментарии:

1. project Модель не имеет a belongs_to :user , поэтому нет user_id атрибута для проверки.

Ответ №3:

Попробуйте объявить ассоциации как

 has_many :projects, :through => :project_users, :uniq => true
  

Извлеките раздел 4.3.2.21 в http://guides.rubyonrails.org/association_basics.html .

Комментарии:

1. Хорошая попытка, но на самом деле это вообще не обеспечивает уникальность. Цитата из раздела руководства, на который ссылается ссылка: «В приведенном выше случае все еще есть два чтения. Однако person.posts показывает только одно сообщение, потому что коллекция загружает только уникальные записи. » В любом случае попробовал, и на самом деле он даже загружает несколько идентичных записей, так что никакого эффекта вообще.