Руководство о том, как правильно установить валидацию с помощью отношения has_many: through? - PullRequest
0 голосов
/ 08 февраля 2019

Я настроил три модели: User, List и UserList - последняя представляет собой модель соединения между User и List в отношении has_many_through.

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

Технические подробности

У меня есть 3 модели:

class User < ApplicationRecord
  has_many :user_lists
  has_many :lists, through: :user_lists, dependent: :destroy
End

class List < ApplicationRecord
  has_many :user_lists
  has_many :users, through: :user_lists, dependent: :destroy

  # no duplicate titles in the List table
  validates :title, uniqueness: true
End

class UserList < ApplicationRecord
  belongs_to :list
  belongs_to :user

  # a given user can only have one copy of a list item
  validates :list_id, uniqueness: { scope: :user_id }
end

Как видите, я бы хотел, чтобы элементы списка были уникальнымина основании их названия.Другими словами, если пользователь Адам добавляет Список с заголовком «Темный рыцарь», то пользователь Беатрис, добавляющий Список с заголовком «Темный рыцарь», на самом деле не должен создавать новую запись Списка - он должен просто создавать новую / отличнуюАссоциация UserList, указывающая на ранее созданный элемент списка.

(Несколько касательно, но я также добавил уникальный индекс в таблицу, поскольку, как я понимаю, это позволяет избежать условия гонки)

class AddIndexToUserLists < ActiveRecord::Migration[5.2]
  def change
    add_index :user_lists, [:user_id, :list_id], unique: true
  end
end

Вотгде все идет не так.

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

Вот действие контроллера (допустим, что current_user правильнополучает Адама):

# POST /lists
    def create
      @list = current_user.lists.find_or_create_by!(list_params)
    end

Это правильно приводит к созданию новой записи списка и связанной записи UserList.Ура!

Как Адам, если я попытаюсь добавить тот же самый заголовок "Темный рыцарь", в мой список снова ничего не произойдет - в том числе без ошибок на консоли.Ура!

Однако - как пользователь Беатрис, если я войду в систему и попытаюсь добавить «Темного рыцаря» в свой список, я теперь получаю сообщение об ошибке в консоли:

POST http://localhost:3000/api/v1/lists 422 (Unprocessable Entity)

Моя отладка и гипотеза

Если я уберу ограничение уникальности для List.title, эта ошибка исчезнет, ​​и Беатрис сможет добавить «Темный рыцарь» в свой список.

Тем не менее, List содержит две записи под названием «Темный рыцарь», которые кажутся излишними.

Что касается Адама, то, возможно, current_user.lists.find_or_create_by!(list_params) в моем действии контроллера находит существующее "Темное"Рыцарь "список, связанный с моим текущим пользователем, и понимая, что он существует - таким образом, не вызывая действие создания.

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

Однако это действие создания пытается создать новый элемент списка с уже существующим заголовком.- то есть это противоречит проверке уникальности модели List.rb.

Я не уверен, как изменить это действие find_or_create_by или проверки модели, чтобы обеспечить для Beatrice новую запись / ассоциацию UserListсоздается, но не новая запись списка (поскольку она уже существует).

Такое чувство, что, может быть, я упускаю что-то простое здесь.А может и нет.Был бы очень признателен за некоторые рекомендации о том, как действовать.Спасибо!

1 Ответ

0 голосов
/ 08 февраля 2019

Я на 99% уверен, что то, что происходит, current_user.lists.find_or_create_by будет искать только те записи List, для которых у пользователя есть запись в UserList.Таким образом, если список существует, но текущий пользователь не имеет связи с ним, он попытается создать новый список, который будет конфликтовать с существующим.

Предполагая, что это проблема, вам нужно найтиСписок независимо от ассоциаций пользователей: @list = List.find_or_create_by(list_params)

Получив этот список, вы можете создать запись UserList через ассоциации или модель UserList.Если вы ищете краткость, я думаю, что вы можете использовать current_user.lists << @list для создания UserList, но вы должны проверить, как это ведет себя, если у пользователя уже есть UserList для этого списка, я не уверен, что он перезапишет ваш существующийdata.

Таким образом (при условии, что метод << работает должным образом для создания UserList) ваше действие контроллера может выглядеть так: </p>

def create
    @list = List.find_or_create_by!(list_params)
    current_user.lists << @list
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...