与 Rails 的双向 many-to-many 关联:如何从双向创建?

Bidirectional many-to-many association with Rails : how to create from both ways?

我对 Rails 上 Ruby 中的 many-to-many 关联有疑问。

我的应用程序中有 3 个模型:与 manu-to-many 关联关联的主题、会议和待办事项。

class Todo < ApplicationRecord
  belongs_to :topic
  belongs_to :meeting
end

然后

class Meeting < ApplicationRecord
  has_many :todos
end

class Topic < ApplicationRecord
  has_many :todos
end

我让我的路线和控制器能够通过会议创建新的待办事项:

Rails.application.routes.draw do
  resources :meetings, only: [:index, :show, :new, :create, :edit, :update]  do
    resources :todos, only: [:index, :new, :create]
  end
  resources :todos, only: [:index, :show, :edit, :update, :destroy]
end

class TodosController < ApplicationController

  def new
    @topic = Topic.find(params[:topic_id])
    @todo = Todo.new
  end

  def create
    @todo = Todo.new(todo_params)
    @meeting = Meeting.find(params[:meeting_id])
    @todo.meeting = @meeting
    @todo.save
    redirect_to meeting_path(@meeting)
  end

  private
  def todo_params
    params.require(:todo).permit(:topic_id, :meeting_id, :note, :deadline, :title)
  end
end

我的观点:

<h3><%= @meeting.date %></h3>
<%= simple_form_for [@meeting, @todo] do |f| %>
  <%= f.input :title %>
  <%= f.input :note %>
  <%= f.date_field :deadline %>
  <%= f.association :topic, label_method: :nom, value_method: :id %>
  <%= f.submit "Add a todo" %>
<% end %>

我的问题是我希望能够通过主题创建待办事项以及添加路由时:

  resources :topics, only: [:index, :show, :new, :create] do
    resources :todos, only: [:index, :new, :create]
  end

当我试图完成我的控制器并对其进行测试时,它似乎很棘手。如果我添加:

@topic = Topic.find(params[:topic_id])

然后它告诉我它需要开会...

有什么想法吗?

您可以创建不同的路线:

resources :meetings do
  resources :todos, only: [:index, :new, :create]
end

resources :topics do
  resources :todos, only: [:index, :new, :create]
end

您可以通过使用路由问题来避免重复:

concerns :todoable do
  resources :todos, only: [:index, :new, :create]
end

resources :topics, concerns: :todoable
resources :meetings, concerns: :todoable

在您的控制器中,您可以检查是否存在 meeting_idtopic_id 参数:

class TodosController < ApplicationController
  before_action :set_parent

  def new
    @todo = Todo.new
  end

  def create
    @todo = @parent.todos.new(todo_params)
    if @todo.save
      redirect_to @parent
    else
      render :new
    end
  end

  private

  def parent_class
    @parent_class ||= if params[:meeting_id].present?
      Meeting
    else if params[:topic_id].present?
      Topic
    else
      # raise an error?
    end
  end

  def set_parent
    id = params["#{parent_class.model_name.param_key}_id"]
    @parent = parent_class.find(id)
  end

  def todo_params
    params.require(:todo)
          .permit(:topic_id, :meeting_id, :note, :deadline, :title)
  end
end
<%= simple_form_for [@parent, @todo] do |f| %>
  <%= f.input :title %>
  <%= f.input :note %>
  <%= f.date_field :deadline %>
  <%= f.association :topic, label_method: :nom, value_method: :id if @parent.is_a?(Meeting) %>
  <%= f.association :meeting, label_method: :nom, value_method: :id if @parent.is_a?(Topic) %>
  <%= f.submit "Add a todo" %>
<% end %>