ruby on rails - Cambio del tipo de clase ActiveRecord en rieles con herencia de tabla única
ruby-on-rails single-table-inheritance (3)
Tengo dos tipos de clases:
BaseUser < ActiveRecord::Base
y
User < BaseUser
que actúa_como_auténtico usando el sistema de autenticación de Authlogic. Esta herencia se implementa usando herencia de tabla única
Si un nuevo usuario se registra, lo registro como Usuario. Sin embargo, si ya tengo un Usuario Base con el mismo correo electrónico, me gustaría cambiar ese Usuario Base a un Usuario en la base de datos sin simplemente copiar todos los datos al Usuario desde el Usuario Base y crear un nuevo Usuario (es decir, con un nuevo Usuario). carné de identidad). es posible? Gracias.
La respuesta de Steve funciona, pero dado que la instancia es de clase BaseUser
cuando se llama a save
, las validaciones y devoluciones de llamada definidas en User
no se ejecutarán. Probablemente quiera convertir la instancia utilizando el método becomes :
user = BaseUser.where(email: "[email protected]").first_or_initialize
user = user.becomes(User) # convert to instance from BaseUser to User
user.type = "User"
user.save!
Puede simplemente configurar el campo de tipo a ''Usuario'' y guardar el registro. El objeto en memoria seguirá mostrándose como un Usuario Base pero la próxima vez que vuelva a cargar el objeto en memoria será un Usuario
>> 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
Según las otras respuestas, esperaba que esto funcionara en 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
No fue así, ya que el cambio de tipo no persistiría. En cambio, opté por este enfoque menos elegante, que funciona correctamente:
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
Y aquí están las pruebas de controlador que utilicé para confirmar la solución:
describe ''Single Table Inheritance (STI)'' do
class String
def articleize
%w(a e i o u).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