При создании записи скопируйте атрибут из связанной записи

#ruby-on-rails

#ruby-on-rails

Вопрос:

В моем приложении Rails to-do у меня есть модель задач. Задачи могут быть blocked_by друг другом. У каждой задачи есть пользователь. Когда я это сделаю taskA.blocked_by.create(name: "Task B") , я бы хотел, чтобы задача B получила того же пользователя, что и задача A.

Проблема в том, что я не могу понять, как ссылаться на запись, которая создает текущую запись. Мне нужно узнать, как получить, taskA.user чтобы я мог автоматически назначать его taskB . Я бы предпочел, чтобы мне не приходилось делать это вручную каждый раз, когда я создаю задачу blocked_by.

Я пробовал устанавливать self.user в before_validation методе.

 before_validation :inherit_user, on: :create

private
    def inherit_user
        if self.user == nil 
            p "Task #{self.name} missing user"
            if self.blocked_by.count > 0
                self.user = self.blocked_by.first.user
                p "Inheriting user from first blocked_by task #{self.blocked_by.first.name}"
            end
        end
    end
  

Это не работает, потому что self.blocked_by является пустым, поскольку запись еще не сохранена.

Документация Rails по методам ассоциативного класса наводит меня на мысль, что я должен быть в состоянии сделать что-то вроде этого:

 has_many :blocked_by do |owner|
    def create(attributes)
        p owner # access properties from association owner here
        attributes.push(user: owner.user)
        super
    end
end
  

Когда я пытаюсь это сделать, я получаю:

 NoMethodError (undefined method `owner' for #<ActiveRecord::Associations::CollectionProxy []>)
  

Редактировать: Вот мой файл модели:

 class Task < ApplicationRecord
    validates :name, :presence => true

    belongs_to :user

    has_many :children, class_name: "Task", foreign_key: "parent_id"
    belongs_to :parent, class_name: "Task", optional: true
    has_ancestry

    # thanks to https://medium.com/@jbmilgrom/active-record-many-to-many-self-join-table-e0992c27c1e
    has_many :blocked_blocks, foreign_key: :blocker_id, class_name: "BlockingTask"
    has_many :blocked_by, through: :blocked_blocks, source: :blocking, dependent: :destroy

    has_many :blocker_blocks, foreign_key: :blocked_id, class_name: "BlockingTask"
    has_many :blocking, through: :blocker_blocks, source: :blocker, dependent: :destroy

    has_many_attached :attachments

    before_validation :inherit_user, on: :create

    def completed_descendants
        self.descendants.where(completed: true)
    end

    def attachment_count
        self.attachments.count
    end

    private
        def inherit_user
            if self.user == nil and self.parent
                self.user = self.parent.user
            end
        end

end
  

Я могу inherit_user из родительской задачи, вот так: taskA.children.create(name: "Task B") . Я хотел бы сделать то же самое для blocked_by отношения.

Ответ №1:

Чтобы обратиться к текущей записи, которая должна быть создана, попробуйте выполнить before_create обратный вызов.

  before_create :inherit_user
  

и self.blocked_by теперь должно быть значение.

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

1. Когда я меняю:inherit_user с before_validate на before_create, модель не проходит проверку, и обратный вызов вообще не выполняется.

2. Мне нужно взглянуть на ваши проверки, а также на то, как вы получаете значение blocked_by (если вы не запрашиваете его у пользователя, то оно должно быть пустым, и вам нужно определить его, прежде чем вы сможете использовать его внутри инструкции if). Если это не так, покажите мне файл вашей модели и метод create.

3. Я отредактировал файл моей модели в вопросе. Выяснить, как определить значение blocked_by — это именно то, что я пытаюсь сделать. У меня нет пользовательского метода создания.

4. Чтобы получить имя пользователя задачи, которую вы можете использовать Task.includes(:user).first.user.name Она берет первую задачу в базе данных и включает пользователя, которому она принадлежит, и возвращает ее имя, измените синтаксис в соответствии с задачей, которая вам нужна. Надеюсь, это то, что вы ищете.