10

Ich habe eine Reihe von STI-Unterklassen erben von einer User Basisklasse. Ich finde, dass unter bestimmten Bedingungen innerhalb einer Unterklassendefinition Abfragen der Unterklassen die type Bedingung nicht korrekt verwenden.ActiveRecord: Abfrage nicht mit korrekten Typ Bedingung für STI-Unterklasse

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

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

Wenn die Rails-Konsole in Entwicklung Laden, tut es das, was ich erwarten würde:

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

Aber wenn die App schlagen (localhost/pow), wird die type Bedingung fehlt und ich diese bekommen :

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

Aber nicht von der App als wenn sie auf einen Testserver zum Einsatz:

Dies führt natürlich dazu, dass alle Abfragen, die hier in der Dev-App (aber nicht von der Konsole) ausgeführt werden, falsch sind. Insbesondere versuche ich, einen (kleinen) Cache existierender DB-Werte vorab zu laden, um basierend auf diesen Daten einige hilfreiche Methoden zu erstellen. Ohne den Typbereich ist der Cache offensichtlich falsch!

Von der gleichen Stelle (Admin), wir folgenden verwirrend Widerspruch erhalten:

[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`" 

Ferner definierte ich eine Wegwerf-Unterklasse Q < User in der user.rb Datei. Ich loggte Q.all.to_sql von seiner Definition, von der Definition von , und von einer Ansicht. In dieser Reihenfolge, erhalten wir:

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') 

Was in der ersten Zeile der Admin Unterklassendefinition in admin.rb die dazu führen könnten, jede Unterklasse von User zu scheitern seine type_condition zu benutzen?

Dies führt dazu, dass Entwicklungstests fehlschlagen, und dies hat auch Auswirkungen auf meine App. Was in aller Welt könnte diesen Unterschied im Verhalten verursachen? Kann jemand an eine allgemeinere Weise um das Problem denken, die STI-Bedingungen in einer Unterklasse während seiner Definition nur in der Entwicklungs-App-Umgebung nicht definiert zu haben?

Antwort

4

Ein Unterschied zwischen der Produktion und Entwicklung ist die folgende Zeile innerhalb der Anwendungskonfiguration:

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

gegen

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

So in der Produktionsumgebung, die alle Ihre clases geladen werden, wenn die App ist gestartet. Wenn auf false gesetzt ist, versucht Rails, die Klasse User automatisch zu laden, wenn Sie zuerst die Klasse Admin laden.

Gegeben, würde ich annehmen, dass Sie eine andere Klasse oder ein Modul mit dem Namen User haben.

FYI: ActiveRecord hat eine Methode namens finder_needs_type_condition?.Es sollte für eine Klasse true zurück, die STI verwendet:

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

Danke für die Tipps. Ich bekomme den großen Lastunterschied, aber es ist immer noch mysteriös, warum das Problem * nicht * in der Dev-Konsole vorhanden ist, aber * in der Dev-App * vorhanden ist (wenn ich localhost/pow verwende). Die 'finder_needs_type_condition?' Methode ist gut zu wissen: aber, es gibt 'true' zurück, wenn ich aus der App hebe, während ich noch" SELECT 'users'. * FROM' users' "' wie in der Frage gebe. Entsprechend fand ich die private Methode 'type_condition', die beim Aufruf von' to_sql' '' '' 'user''type' IN ('Admin') '' 'ergibt. Also ist etwas eindeutig kaputt. –

+0

Related, ich war ** nicht ** in der Lage, dieses Verhalten in einer anderen STI-Unterklasse einer komplett anderen Basisklasse zu replizieren, also scheint dies speziell mit der Klasse 'User' zu tun zu haben. Progress ... –

+0

Ein weiteres Update: Ich habe * alles * in meiner 'User' Basisklasse kommentiert anders als die Klassen-Deklaration selbst, und das Problem besteht weiter. So kann ich jetzt nicht sehen, was an dieser Klasse besonders sein könnte oder wie das, was dieses Problem verursacht, verschwindet, nachdem die Klassendefinition abgeschlossen ist. –

1

Derzeit jede User eine leere :type Spalte haben. Könnte das ein Problem sein?

Haben Sie versucht, User eine Superklasse zu machen? Z.B.

class User < ActiveRecord::Base 
end 

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

Korrigieren Sie mich, wenn ich etwas falsch verstanden habe, ich habe 'abstract_class' nie in Ruby/Rails verwendet. Wenn ich die Dokumente lese, sehe ich, dass "abstract_class" auf "true" gesetzt werden sollte, wenn ich nicht möchte, dass die Unterklassen den gleichen Tabellennamen verwenden. Aber ich tue es, also bin ich mir nicht sicher, ob das für meinen Anwendungsfall richtig ist. Wenn ich es versuche, bekomme ich, was Sie dann erwarten könnten: 'ActiveRecord :: StatementInvalid: Mysql2 :: Fehler: Tabelle 'threeplay_development.admins' existiert nicht '. Was meinst du mit "Benutzer" wird eine leere Spalte "Typ" haben? Die Tabelle "Benutzer" ist korrekt ausgefüllt. Ich muss diese Daten nur laden, wenn die App gestartet wird. –

+0

Upps, du hast Recht mit der 'abstract_class': Das war Unsinn. Das Problem mit dem ** leeren ** ': type '-Spalte. Wenn Sie 'User.create ({attributes ....})' ohne Angabe von ': type' ausführen, erhält es einen' nil' Wert. Das selbe mit 'Admin.create (...)' setzt automatisch 'type = 'Admin'. – andiba

+0

Ja, danke für die Antwort. Die Daten sind bereits vorhanden und die Spalte "type" ist ausgefüllt (aktuell 13.000 insgesamt 'User's, darunter 23' Admin's). Hier lese ich nur aus der Tabelle mit 'Admin.all' (oder' self.all' in der Definition der 'Admin'-Klasse), und es erzeugt keine passende' WHERE'-Klausel unter der oben beschriebenen spezifischen Bedingung. –

Verwandte Themen