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_id
和 calendar_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
在我们的 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
.
但是,似乎没有创建新的 @administration
并且管理 table 没有更新,因为当我们在控制台中键入 Administration.all
时什么都没有 returns .
我们认为管理 table,它是用户 table 和日历 table 之间的联接 table,并且分别包含 user_id
和 calendar_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