Rails has_many :through association: save instance into join table

Rails has_many :through association: save instance into join table

在我们的 Rails 应用程序中,有 3 个模型:

class User < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :calendars, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Calendar < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :users, through: :administrations
end

这里是相应的迁移:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name
      t.string :email

      t.timestamps null: false
    end
  end
end

class CreateAdministrations < ActiveRecord::Migration
  def change
    create_table :administrations do |t|
      t.references :user, index: true, foreign_key: true
      t.references :calendar, index: true, foreign_key: true
      t.string :role

      t.timestamps null: false
    end
  end
end

class CreateCalendars < ActiveRecord::Migration
  def change
    create_table :calendars do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end

我们创建了 calendar#create 动作如下:

def create
    @calendar = current_user.calendars.build(calendar_params)
    if @calendar.save
      flash[:success] = "Calendar created!"
      redirect_to root_url
    else
      render 'static_pages/home'
    end
  end

和对应的_calendar_form.html.erb部分:

<%= form_for(@calendar) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_field :name, placeholder: "Your new calendar name" %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

这 "works" 因为,当我们通过表单创建新日历时,当我们键入 Calendar.all.

时,它会显示在 Rails 控制台中

但是,似乎没有创建新的 @administration 并且管理 table 没有更新,因为当我们在控制台中键入 Administration.all 时什么都没有 returns .

我们认为管理 table,它是用户 table 和日历 table 之间的联接 table,并且分别包含 user_idcalendar_id 列,将在创建新日历时自动更新。

我们怎样才能做到这一点?我们需要创建特定的 administration#create 操作吗?

更新:根据评论和答案,我们实施了以下 CalendarsController

class CalendarsController < ApplicationController

  def create
    @calendar = current_user.calendars.build(calendar_params)
    if @calendar.save
      current_user.administrations << @calendar
      @calendar.administration.role = 'creator'
      flash[:success] = "Calendar created!"
      redirect_to root_url
    else
      render 'static_pages/home'
    end
  end

...

但是,这个returns出现如下错误:

ActiveRecord::AssociationTypeMismatch in CalendarsController#create

unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
            message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
            raise ActiveRecord::AssociationTypeMismatch, message
          end
        end

app/controllers/calendars_controller.rb:6:in `create'

我们是不是漏掉了什么?

是的,你绝对做到了!你没有得到一个新的 Administration 对象,因为你没有向系统请求一个。

如果您真的非常需要在每次创建 Calendar 时都创建一个新的 Administration 对象,您可以这样做:

class Calendar < ActiveRecord::Base
  attr_reader :administration

  def initialize(params)
    @administration = Administration.new(params[:id])
  end
end

class Administration < ActiveRecord::Base
  def initialize(id_param_from_calendar)
    calendar_id = id_param_from_calendar
  end
end

显然这不是最佳做法,因为它会在您的代码中注入依赖项。现在,无论如何,您的 Calendar class 知道 Administration class 不仅存在,而且使用其 id 列进行初始化。这只是回答您问题的示例。

另一个例子是像这样在你的 Calendar class 中包含一个 after_initialize 挂钩(请注意 autosave: true 这对于确保自动保存子对象):

class Calendar < ActiveRecord::Base
  has_many :administrations, autosave: true, dependent: :destroy

  after_initialize :init_admins

  def init_admins
    # you only wish to add admins on the newly instantiated Calendar instances
    if new_record?
      3.times { Administration.new self }
    end
  end

end

此外,您还可以将上述逻辑包含在 after_create 挂钩中。唯一的区别是 after_create 发生在对象创建之后,而 after_initialize 发生在对象被实例化或从数据库加载之后。

如果您使用current_user.calendars.build(calendar_params),您只会得到新的日历,没有管理。

如果您使用 current_user.calendars.create(calendar_params),您将把管理和日历都保存到数据库中。

如果想先查看日历是否保存成功,我用的是这个方法:

def create
  @calendar = Calendar.build(calendar_params)
  if @calendar.save
    current_user.administrations << @calendar
    flash[:success] = "Calendar created!"
    redirect_to root_url
  else
    render 'static_pages/home'
  end
end

更新:

将日历关联到用户时出错。正确的应该是:

def create
  @calendar = current_user.calendars.build(calendar_params)
  if @calendar.save
    current_user.calendars << @calendar
    flash[:success] = "Calendar created!"
    redirect_to root_url
  else
    render 'static_pages/home'
  end
end