Changer le type de classe ActiveRecord dans les Rails avec un héritage de Table simple

j'ai deux types de classes:

BaseUser < ActiveRecord::Base 

et

User < BaseUser

quel acts_as_authentic utilise le système D'authentification D'Authlogic. Cet héritage est implémenté en utilisant L'héritage de Table unique

Si un nouvel utilisateur s'enregistre, de l'inscrire en tant qu'Utilisateur. Cependant, si J'ai déjà un BaseUser avec le même email, je voudrais changer ce BaseUser en un utilisateur dans la base de données sans simplement copier toutes les données à L'utilisateur à partir du BaseUser et créer un nouvel Utilisateur (c'est à dire avec un nouvel id). Est-ce possible? Grâce.

25
demandé sur Wei 2010-07-16 10:28:17
la source

3 ответов

Vous pouvez simplement définir le champ de type à 'User' et enregistrer l'enregistrement. L'objet en mémoire sera toujours un BaseUser mais la prochaine fois que vous rechargez l'objet en mémoire sera un Utilisateur

>> 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
18
répondu Steve Weet 2010-07-16 12:19:45
la source

la réponse de Steve fonctionne mais puisque l'instance est de classe BaseUser quand save est appelé, validations et callbacks définis dans User ne fonctionnera pas. Vous voudrez probablement convertir l'instance en utilisant le devient méthode:

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!
74
répondu balexand 2012-12-24 04:47:34
la source

D'après les autres réponses, je m'attendais à ce que cela fonctionne dans les 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

il ne l'a pas fait, car le changement de type ne persisterait pas. Au lieu de cela, j'ai opté pour une approche moins élégante, qui fonctionne correctement:

  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

Et voici le contrôleur de test, j'ai utilisé pour confirmer la solution:

  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
4
répondu dankohn 2014-09-29 06:11:43
la source