25

Ich habe zwei Arten von Klassen:ändern Art von Active Klasse in Rails mit Single Table Inheritance

BaseUser < ActiveRecord::Base 

und

User < BaseUser 

die Authlogic Authentifizierungssystem acts_as_authentic verwenden. Diese Vererbung wird unter Verwendung der Vererbung der einzelnen Tabellen implementiert.

Wenn sich ein neuer Benutzer anmeldet, registriere ich ihn als Benutzer. Wenn ich jedoch bereits einen BaseUser mit derselben E - Mail habe, möchte ich diesen BaseUser zu einem Benutzer in der Datenbank ändern, ohne einfach alle Daten vom BaseUser auf den Benutzer zu kopieren und einen neuen Benutzer zu erstellen (dh mit einem neuen Ich würde). Ist das möglich? Vielen Dank.

Antwort

18

Sie können einfach das Feld Typ auf "Benutzer" einstellen und den Datensatz speichern. Das Objekt im Speicher wird zeigen, immer noch als BaseUser aber das nächste Mal neu zu laden Sie das In-Memory-Objekt

>> 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 
+3

Ihre Antwort für mich funktioniert gut, aber es ist die Validierungen der 'User'-Klasse nicht laufen, kannst du mir bitte dabei helfen? – abhas

72

Steves Antwort Werke einen Benutzer sein, aber da die Instanz der Klasse ist BaseUser wenn save heißt, Validierungen und Callbacks, die in User definiert sind, werden nicht ausgeführt. Sie werden wahrscheinlich die Instanz mit der becomes Methode konvertieren möchten:

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! 
+0

Beim Experimentieren mit 'rails c' in Rails 3.2.18 musste ich' user.becomes' nicht aufrufen, damit die Validierungen der neuen Klasse ausgeführt werden konnten. Bist du sicher, dass das korrekt ist? – Kevin

+19

Sie können 'user = user.becomes! (User)' (mit einem '!') Verwenden und die Zeile 'user.type =" User "'weglassen. –

+1

Das ist großartig. Nie gehört von dieser Methode irgendwo – urmurmur

4

Basierend auf den anderen Antworten, erwartete ich das 4.1 in Rails zu arbeiten:

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 

Es hat nicht, wie der Typ Veränderung würde nicht bestehen bleiben. Stattdessen ging ich mit dieser weniger elegante Methode, die richtig funktioniert:

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 

Und hier sind die Controller Tests, die ich verwendet, um die Lösung zu bestätigen:

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 
+0

Dank, scheint dies funktioniert, mit einer kleinen Änderung: 'new_type = params [: Auflisten]!? [: Type] wenn new_type = @ listing.type && Eintrag :: LISTING_TYPES.include (new_type) # we ‚Typen Klasse wieder zu ändern hier, müssen so vorsichtig sein @ listing.update_column: Typ, new_type @listing = new_type.constantize.find (@ listing.id) end' ich manuell die Instanz neu zu laden benötigt (Aufruf "reload" funktionierte in meinem Fall nicht – elsurudo

Verwandte Themen