#ruby-on-rails
#ruby-on-rails
Вопрос:
Мне нужна возможность добавления новой задачи в существующий список (проект) или в новый. У меня есть 2 модели: Project и Todo. Project has_many :todos, Todo belongs_to:project Я написал некоторый код, который дает мне возможность создавать задачи
class Api::V1::TodosController < ApiController
skip_before_action :verify_authenticity_token
def create
@todo = Todo.new(todo_params_on_create)
if @todo.save
render json: @todo, status: :created
else
render json: @todo.errors, status: :unprocessable_entity
end
end
private
def todo_params_on_create
params.require(:todo).permit(:text, :checked, :project_id)
end
end
Но я могу создать задачу только для существующего проекта из-за ассоциации, и мне нужно передать project_id в запросе todos
Не могли бы вы помочь, как я могу реализовать создание новой задачи с помощью нового проекта?
PS Я подумал, что мне нужно реализовать какой-то метод в модели проекта и вызвать его перед сохранением новой задачи. Может быть, что-то вроде этого (но передайте значение заголовка в запросе todos):
class Project < ApplicationRecord
has_many :todos
before_save :new_project, unless: :self.nil?
def new_project
self.update_columns(title: "NewProject")
end
end
Но я думаю, что я ошибаюсь в своих мыслях.
Комментарии:
1. Использовать
belongs_to :project, optional: true
в модели задач.2. @kishorecheruku спасибо за совет! Как я могу создать задачу в связи с новым проектом? Чтобы при создании новой задачи одновременно создавался новый проект с заполненным значением заголовка, а новая задача была связана с новым проектом.
3. Вы хотите иметь nested_attributes для модели проекта. Вот хороший пример с объяснением и примером.
Ответ №1:
Для создания элемента Todo без проекта используйте, как указано в комментарии belongs_to :project, optional: true
Для создания вашего элемента Todo вместе с новым проектом
Некоторые быстрые грязные способы заключаются в том, чтобы просто создать объект Project перед созданием объекта Todo в вашем контроллере Todo (если вы хотите, чтобы он был создан только с помощью этого контроллера)
class Api::V1::TodosController < ApiController
def create
todo = Todo.new(todo_params_on_create)
if todo.project_id.nil?
project = Project.create(title: "NewProject")
todo.project_id = project.id
end
if todo.save
render json: todo, status: :create
else
render json: todo.errors, status: :unprocessable_entity
end
end
end
или в вашей модели Todo (если вы хотите использовать ее всякий раз, когда создается задача Todo)
class Todo < ApplicationRecord
before_create :create_project, unless: :project_id
def create_project
project = Project.create(title: "NewProject")
self.project_id = project.id
end
end
Проблема с этим подходом заключается в том, что в случае сбоя создания объекта Todo в вашей базе данных все равно будет создан объект Project. Решением этой проблемы было бы удаление объекта Project в случае сбоя, но это все равно может привести к ошибкам.
Более правильным решением является просто использовать транзакции и обработку ошибок для обработки создания / удаления объектов, но это также сопряжено с дополнительными накладными расходами:
class Api::V1::TodosController < ApiController
def create
begin
ActiveRecord::Base.transaction do
todo = Todo.new(todo_params_on_create)
if todo.project_id.nil?
project = Project.create!(title: "NewProject")
todo.project_id = project.id
end
todo.save!
render json: todo, status: :create
end
rescue StandardError => e
//Handle error message
end
end
end
Комментарии:
1. Спасибо за совет! И как я могу вместо жестко заданного заголовка передать значение заголовка в запросе при создании задачи?
2.
title: params[:title]
если у вас есть заголовок, переданный в качестве параметра