Rails has_many:通过关联:同时更新所有3个模型

时间:2015-07-02 19:22:45

标签: ruby-on-rails ruby-on-rails-4 model-view-controller orm relational-database

这个问题在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.integer :total_calendar_count
      t.integer :owned_calendar_count

      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

以下是我们要完成的任务:

当登录用户(current_user)创建日历时,我们应该:

  • 创建新的@calendar并将其保存到日历表
  • 分配&#34;创作者&#34;通过“管理”表中的“角色”列为此新创建的日历创建用户角色(current_user)
  • 增加用户表的total_calendar_countowner_calendar_count

为了做到这一点,我们认为我们需要处理日历#create。

在CalendarsController中,我们已经有了以下代码:

def create
    @calendar = current_user.calendars.create(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 "Create", class: "btn btn-primary" %>
<% end %>

我们正在考虑按如下方式更新控制器:

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

ActiveRecord::AssociationTypeMismatch in CalendarsController#create
Administration(#70307724710480) expected, got Fixnum(#70307679752800)

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:7:in `create'

我们怎样才能让它发挥作用?

1 个答案:

答案 0 :(得分:1)

此行实际上导致错误:current_user.administrations << @calendar.id

当您将current.administrations传递给它时,

Administration需要Fixnum类型的对象。

您可以通过以下方式完成相同的功能:

current_user.administrations.create(calendar_id: @calendar.id)

修改

OP在评论中提到这是一个好习惯。看,有规则说控制器应该是瘦的,模型应该是胖的。嗯,这意味着你应该尝试编写最少的代码,所有逻辑和提取对象应该在那里楷模。但在您的代码方案中并非如此。您应该将代码移动到模型中,然后将其调用到控制器中。

以下是:

class User < ActiveRecord::Base
  def add_calendar_and_role(calendar_id, role)
    self.administrations.find_by(calendar_id: calendar_id).update(role: role)
  end
end

这样,您的代码简化为:

current_user.add_calendar_and_role(@calendar.id, 'Creator')

同样,您可以进一步重构控制器代码。