使用单表inheritance更改Rails中的ActiveRecord类的类型

我有两种类:

BaseUser < ActiveRecord::Base 

 User < BaseUser 

其中acts_as_authentic使用Authlogic的身份validation系统。 此inheritance是使用单表inheritance实现的

如果新用户注册,我将他注册为用户。 但是,如果我已经有一个具有相同电子邮件的BaseUser,我想将该BaseUser更改为数据库中的用户,而不是简单地将所有数据从BaseUser复制到用户并创建新用户(即使用新用户) ID)。 这可能吗? 谢谢。

您只需将类型字段设置为“用户”并保存记录即可。 内存中对象仍将显示为BaseUser,但下次重新加载内存中对象时将是User

 >> b=BaseUser.new >> b.class # = BaseUser # Set the Type. In-Memory object is still a BaseUser >> b.type='User' >> b.class # = BaseUser >> b.save # Retrieve the records through both models (Each has the same class) >> User.find(1).class # = User >> BaseUser.find(1).class # User 

Steve的答案有效,但由于实例是在调用BaseUser时类BaseUser ,因此User定义的validation和回调将不会运行。 您可能希望使用become方法转换实例:

 user = BaseUser.where(email: "user@example.com").first_or_initialize user = user.becomes(User) # convert to instance from BaseUser to User user.type = "User" user.save! 

根据其他答案,我希望这在Rails 4.1中有效:

  def update @company = Company.find(params[:id]) # This separate step is required to change Single Table Inheritance types new_type = params[:company][:type] if new_type != @company.type && Company::COMPANY_TYPES.include?(new_type) @company.becomes!(new_type.constantize) @company.type = new_type @company.save! end @company.update(company_params) respond_with(@company) end 

它没有,因为类型的改变不会持续。 相反,我选择了这种不太优雅的方法,它可以正常工作:

  def update @company = Company.find(params[:id]) # This separate step is required to change Single Table Inheritance types new_type = params[:company][:type] if new_type != @company.type && Company::COMPANY_TYPES.include?(new_type) @company.update_column :type, new_type end @company.update(company_params) respond_with(@company) end 

以下是我用来确认解决方案的控制器测试:

  describe 'Single Table Inheritance (STI)' do class String def articleize %w(aeiou).include?(self[0].to_s.downcase) ? "an #{self}" : "a #{self}" end end Company::COMPANY_TYPES.each do |sti_type| it "a newly assigned Company of type #{sti_type} " \ "should be #{sti_type.articleize}" do post :create, { company: attributes_for(:company, type: sti_type) }, valid_session expect(assigns(:company)).to be_a(sti_type.constantize) end end Company::COMPANY_TYPES.each_index do |i| sti_type, next_sti_type = Company::COMPANY_TYPES[i - 1], Company::COMPANY_TYPES[i] it "#{sti_type.articleize} changed to type #{next_sti_type} " \ "should be #{next_sti_type.articleize}" do company = Company.create! attributes_for(:company, type: sti_type) put :update, { id: company.to_param, company: { type: next_sti_type } }, valid_session reloaded_company = Company.find(company.to_param) expect(reloaded_company).to be_a(next_sti_type.constantize) end end end