2016-05-02 44 views
10

Ho una serie di sottoclassi STI che ereditano da una classe User base. Sto scoprendo che in determinate condizioni all'interno di una definizione sottoclasse, le query sui sottoclassi non utilizzano correttamente la condizione type.ActiveRecord: query non utilizza corretta condizione tipo per STI sottoclasse

class User < ActiveRecord::Base 
    # ... 
end 

class Admin < User 
    Rails.logger.info "#{name}: #{all.to_sql}" 
    # ... 
end 

Quando si carica la console Rails in fase di sviluppo, si fa quello che ci si aspetterebbe:

Admin: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Admin') 

Ma quando colpisce l'app (localhost/Pow), manca la condizione type e ottengo questo :

Admin: SELECT `users`.* FROM `users` 

Ma non attraverso l'app quando quando distribuito a un server di gestione temporanea:

Admin: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Admin') 

Questo, naturalmente, fa sì che qualsiasi query eseguite qui in app dev (ma non dalla console) per non essere corretto. Nello specifico, sto provando a precaricare una (piccola) cache di valori db esistenti per creare alcuni metodi utili basati su quei dati. Senza lo scope del tipo, la cache è ovviamente errata!

Dalla stessa posizione (Admin), otteniamo la seguente contraddizione confusione:

[11] pry(Admin)> Admin.finder_needs_type_condition? 
=> true 
[12] pry(Admin)> Admin.send(:type_condition).to_sql 
=> "`users`.`type` IN ('Admin')" 
[13] pry(Admin)> Admin.all.to_sql 
=> "SELECT `users`.* FROM `users`" 

Inoltre, ho definito una sottoclasse usa e getta Q < User all'interno del file user.rb. Mi sono collegato Q.all.to_sql dalla sua definizione, dalla definizione di Admin, e da una vista. In questo modo, si ottiene:

From Q: Q: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Q') 
From Admin: Q: SELECT `users`.* FROM `users` 
From View: Q: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Q') 

Che cosa potrebbe causare, nella prima riga della definizione Admin sottoclasse in admin.rb, qualsiasi sottoclasse di User non riuscire a utilizzare il suo type_condition?

questo sta causando prove di sviluppo di fallire, e così è di una certa importanza per la mia app. Cosa diavolo potrebbe causare questa differenza di comportamento? Qualcuno può pensare a un modo più generale intorno al problema di non avere le condizioni STI definite su una sottoclasse durante la sua definizione solo nell'ambiente di sviluppo app?

risposta

4

Una differenza tra produzione e sviluppo è la seguente riga all'interno della configurazione dell'applicazione:

# config/environments/development.rb 
config.eager_load = false 

vs.

# config/environments/production.rb 
config.eager_load = true 

Così sul vostro ambiente di produzione, tutti i clases vengono caricati quando l'applicazione è iniziato. Quando eager_load è impostato su falso, Rails proverà a caricare automaticamente la classe User quando si carica per la prima volta la classe Admin.

Dato che, presumo che tu abbia un'altra classe o modulo chiamato User.

FYI: ActiveRecord ha un metodo chiamato finder_needs_type_condition?.Dovrebbe restituire vero per una classe che utilizza STI:

User.finder_needs_type_condition? # should be false 
Admin.finder_needs_type_condition? # should be true 
+0

Grazie per i suggerimenti. Ottengo la differenza di carico impaziente, ma è ancora misterioso perché il problema è * non * presente nella console di sviluppo ma * è * presente nell'app dev (quando utilizzo localhost/pow). Il metodo 'finder_needs_type_condition?' È utile sapere: tuttavia restituisce 'true' quando faccio leva dall'app mentre ancora mi dà' 'SELECT' users'. * FROM 'users'" 'come nella domanda. Correttamente, ho trovato il metodo privato 'type_condition', che, quando chiama' to_sql' on, restituisce '' "' users' .type' IN ('Admin') "' '. Quindi qualcosa è chiaramente rotto. –

+0

Correlati, non ero ** in grado di replicare questo comportamento su un'altra sottoclasse STI di una classe base completamente diversa, quindi questo sembra avere qualcosa a che fare con la classe 'User' in particolare. Avanzamento ... –

+0

Un altro aggiornamento: ho commentato * tutto * nella mia classe di base 'Utente' diversa dalla dichiarazione di classe stessa, e il problema persiste. Quindi non riesco a vedere cosa potrebbe essere speciale in questa classe ora, o come qualunque cosa stia causando questo problema scompare al termine della definizione della classe. –

1

Attualmente, qualsiasi User avranno un :type colonna vuota. Potrebbe essere un problema?

Hai provato a fare User una super classe? Per esempio.

class User < ActiveRecord::Base 
end 

class Admin < User; end 
class NormalUser < User; end 
+0

Correggimi se ho frainteso qualcosa, non ho mai usato 'abstract_class' in Ruby/Rails. Leggendo i documenti, vedo che 'abstract_class' dovrebbe essere impostato su' true' se io * non * voglio che le sottoclassi utilizzino lo stesso nome di tabella. Ma io sì, quindi non sono sicuro che sia corretto per il mio caso d'uso. Quando provo, ottengo ciò che potreste aspettarvi: 'ActiveRecord :: StatementInvalid: Mysql2 :: Errore: La tabella 'threeplay_development.admins' non esiste'. Cosa intendevi per "utente" con una colonna 'type' vuota? La tabella 'users' è popolata correttamente, ho solo bisogno di caricare quei dati all'avvio dell'app. –

+0

Upps, hai ragione con 'abstract_class': era una sciocchezza. Tuttavia, il problema della colonna ** empty ** ': type'. Quando fai un 'User.create ({attributes ....})' senza specificare ': type' ottiene un valore' nil'. Lo stesso con 'Admin.create (...)' imposta automaticamente 'type = 'Admin''. – andiba

+0

Sì, grazie per la risposta. I dati esistono già e la colonna 'type' è popolata (corrente 13k totale' User's, tra cui 23 'Admin's). Qui sto solo leggendo dalla tabella usando 'Admin.all' (o' self.all' all'interno della definizione della classe 'Admin'), e non sta generando una clausola 'WHERE' appropriata sotto la condizione specifica descritta sopra. –