Warum Model.find Verhalten in einer entfalteten Umgebung unterscheiden sich von der in einer Entwicklungsumgebung?

StackOverflow https://stackoverflow.com/questions/2214367

Frage

Mit Ruby on Rails in Kombination mit Capistrano und git, ich habe in ein lästiges Problem führen ..

Ich habe einen Controller „Menschen“ mit der Index Aktion, die etwa wie folgt aussieht:

def index
  @people = Person.find( :conditions => params[:search] )
end

Es gibt eine boolean "is_admin" Spalte in der Tabelle Person. Unter der Annahme, dass einige der Leute sind admin und einige sind nicht, ein get Aufruf http : // localhost: 3000 / Leute suchen [is_admin] = true sollte bevöl @people mit einigen Benutzern ... Und das gilt für meine lokalen PC, wenn ich führen Sie die App in Entwicklung Modus ..

Aber .... Wenn ich einsetzen, um meine Server-Konto (railsplayground), einen Aufruf von http://mydomain.com/people?search [is_admin] = true nicht alle Übereinstimmungen ergeben zu finden. Allerdings, wenn ich ... ändern? Suche [is_admin] = true ...? Suche [is_admin] = 1 die Antwort des Administrator Benutzer zurückgegeben, wie erwartet .. .

Zurück auf meinem lokalen PC, die Verwendung von "1" anstelle von "true" fehlschlägt.

Das Endergebnis ist, dass

Person.find( :all, :conditions => { :is_admin => 'true' } )

arbeitet meine Entwicklungsumgebung, und

Person.find( :all, :conditions => { :is_admin => 1 } )

arbeitet in meiner Umgebung eingesetzt.

Warum ist das? und wie kann ich es beheben?

Im Idealfall würde Ich mag Links platzieren wie:

link_to( "Administrators", {
  :controller => '/people',
  :action => :index,
  :search => { :is_admin => true }
})

und erhält eine Liste der Administratoren.)

Könnte würdig zu beachten, dass meine Entwicklung Datenbank eine sqlite3-Datei ist, und die Produktion ist eine MySQL-Datenbank ...

EDIT: Ich verstehe die Einwände gegen die Benutzereingabe, aber in diesem Ausnahmefall ist es eine sehr sehr geringe Gefahr. Auch mein aktueller Code funktioniert perfekt in entweder meiner Entwicklung PC oder mein Produktionskonto, aber nicht beides. Der einfachste sollution scheint die Art und Weise sqlite3 speichert und interperets Boolesche Werte zu ändern, so würde ich meine Frage ändern „Wie ändere ich die Art und Weise SQLite Boolesche Werte speichert“ ... Wenn SQLite würde mysql Verhalten perfekt imitieren, wäre es meine Entwicklungen dienen muss perfekt ...

War es hilfreich?

Lösung

Das Problem ist, dass Sie einen Booleschen Wert als String und das endgültige Verhalten hängt von der aktiven Datenbank sind vorbei. Hier ist eine ausführlichere Erklärung.

Wenn Sie die params[:search] Variable gelesen, der Inhalt ist eine Zeichenfolge, und es gibt keinen Typ. Dies liegt daran, dass der Query-String kann nicht verstehen, ob

params[:search][:is_admin] = "true"

eigentlich bedeutet

params[:search][:is_admin] = "true"
params[:search][:is_admin] = true

Auch wenn Sie 1 passieren Sie am Ende mit

params[:search][:is_admin] = "1"

, die anders ist als

params[:search][:is_admin] = 1

Wenn Sie den Wert auf die Abfrage übergeben, da Sie eine boolean sind vorbei, wird der Wert nicht durch den Datenbankadapter übersetzt. Ihr endgültiges Abfrageer in so etwas wie

SELECT * FROM `persons` WHERE `persons.is_admin` = 'true'

SQLite3 speichert als t / f Strings boolean. true wird gespeichert, wie 't' und false als 'f' gespeichert ist. Ich denke, es versteht auch wahr / falsch und übersetzt sie automatisch Ihre Anfrage. Auf der gegenüberliegenden, versteht MySQL nur 0/1 und wahr / falsch und es funktioniert nicht.

Wenn Sie das Verhalten wechseln, vorbei 1 statt true, Sie verursachen SQLite zum Scheitern verurteilt, weil es nicht die Zeichenfolge "1" 't' übersetzen. Auf der gegenüberliegenden, MySQL kann und es funktioniert.

In beiden Fällen Sie tun es falsch. Sie sollten keinen String, sondern einen Booleschen Wert übergeben. Außerdem sollten Sie niemals Benutzereingaben vertrauen.

Mein Vorschlag ist Ihre params[:search] zu normalisieren, bevor Person.find Fütterung.

Andere Tipps

Ich denke, der Unterschied zwischen der Verwendung von SQLite und MySQL die Ursache für das Problem ist.

Aber aktiven Datensatz erlaubt Anlage aus dem Query-String direkt übergeben zu sein scheint wie eine schlechte Idee zu mir, und könnte die App öffnen, um SQL-Injection-Angriffe verlassen.

@Simone Antwort erklärt, warum Sie ein solches Verhalten zu bekommen. Um ein korrektes Ergebnis in beiden sqlite3 zu bekommen und MySQL sollten Sie übergeben:

Person.find( :all, :conditions => { :is_admin => true } )

Wenn Sie nur eine Ebene der Parameter (Sie haben params[:search][:something] aber nichts tiefer), als Sie richtig Hash mit Bedingungen wie folgt erstellen:

my_conditions = Hash.new
params[:search].each do |item, value|
  my_conditions[item.to_sym] = value == 'true' ? true : value == 'false' ? false : value
end

Es wird alle Such params iterieren und alle 'true' ändern true und 'false' false. Wenn es anderen Wert sein wird, es es zu lassen, wie es ist. Natürlich können Sie jede Logik hier setzen Sie wollen. Wenn es wird noch komplizierter, als Sie es mit if oder mit switch umschreiben können.

Dann können Sie:

Person.all(:conditions => my_conditions)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top