2008-12-11 22 views
20

Ich habe eine Rails-App, mit der ein Benutzer eine Datenbankabfrage erstellen kann, indem er ein umfangreiches Formular ausfüllt. Ich habe mich über die beste Methode zur Überprüfung von Formularparametern in Rails Gedanken gemacht. Früher habe ich meine results Methode (die eine zu dem macht die Form) hatte folgendes tun:Rails Formularvalidierung

if params[:name] && !params[:name].blank? 
    @name = params[:name] 
else 
    flash[:error] = 'You must give a name' 
    redirect_to :action => 'index' 
    return 
end 

Aber für mehrere Formularfelder, sehen dies wiederholt für jede ermüdend bekam. Ich konnte nicht nur halten sie alle in irgendeiner Schleife für jedes Feld zu überprüfen, da die Felder unterschiedlich eingerichtet sind:

  • ein einzelner Schlüssel: params[:name]
  • einen Schlüssel und einen Unterschlüssel: params[:image][:font_size]
  • nur einige Formularfelder erwarten ausgefüllt werden, wenn ein anderes Feld Dieser

etc. war auch wiederholend eingestellt war, weil ich flash[:error] für jede fehlende/ungültige Parametereinstellung, und für jeden auf die gleiche URL umgeleitet wird. Ich wechselte auf eine before_filter, die alle notwendigen Formularparameter überprüft und nur wahr zurückgibt, wenn alles in Ordnung ist. Dann wird das mein results Verfahren geht weiter, und Variablen sind nur flat-out zugewiesen, ohne Kontrolle beteiligt: ​​

@name = params[:name] 

In meinem validate_form Methode habe ich Abschnitte des Codes wie folgt aus:

if (
    params[:analysis_type][:to_s] == 'development' || 
    params[:results_to_generate].include?('graph') 
) 
    {:graph_type => :to_s, :graph_width => :to_s, 
    :theme => :to_s}.each do |key, sub_key| 
    unless params[key] && params[key][sub_key] 
     flash[:error] = "Cannot leave '#{Inflector.humanize(key)}' blank" 
     redirect_to(url) 
     return false 
    end 
    end 
end 

I Ich habe mich nur gefragt, ob ich das am besten mache, oder ob mir etwas offensichtlich ist, wenn es um die Validierung von Parametern geht. Ich mache mir Sorgen, das ist immer noch nicht die effizienteste Technik, weil ich mehrere Blöcke habe, wo ich flash[:error] einen Wert zuweisen, dann auf die gleiche URL umleiten und dann false zurückgeben.

bearbeiten zu klären: Der Grund, warum ich diese Validierung nicht in Modell (s), die derzeit aus zwei Gründen:

  • Ich bin nicht zu sammeln Daten aus dem Benutzer, um versuchen, Erstellen oder aktualisieren Sie eine Zeile in der Datenbank. Keine der Daten, die der Benutzer übermittelt, werden nach dem Abmelden gespeichert. Es wird alles richtig verwendet, wenn sie es einreichen, um die Datenbank zu durchsuchen und einige Sachen zu erzeugen.
  • Das Abfrageformular nimmt Daten zu mehreren Modellen auf und nimmt andere Daten auf, die überhaupt nicht zu einem Modell gehören. Z.B. Diagrammtyp und -thema, wie oben gezeigt, verbinden sich nicht mit irgendeinem Modell, sondern vermitteln lediglich Informationen darüber, wie der Benutzer seine Ergebnisse anzeigen möchte.

bearbeiten verbesserte Technik zeigen: I Verwendung von anwendungsspezifischen Ausnahmen machen Raising the Right Exception article jetzt dank Jamis Buck. Zum Beispiel:

def results 
    if params[:name] && !params[:name].blank? 
    @name = params[:name] 
    else 
    raise MyApp::MissingFieldError 
    end 

    if params[:age] && !params[:age].blank? && params[:age].numeric? 
    @age = params[:age].to_i 
    else 
    raise MyApp::MissingFieldError 
    end 
rescue MyApp::MissingFieldError => err 
    flash[:error] = "Invalid form submission: #{err.clean_message}" 
    redirect_to :action => 'index' 
end 
+0

Ich denke, dass Sie params [: name] und! Params [: name] .blank nicht verwenden müssen? in deinen Bedingungen. ! params [: Name] .blank? ist genug. params [: name] gibt false zurück, wenn es nil ist (es gibt keinen solchen Parameter) und params [: name] .blank? true zurück, wenn es null oder leer ist. – klew

+0

Sie erhalten einen NoMethodError. Ich könnte NilClass überschreiben, um #blank hinzuzufügen? dazu nehme ich an. irb (main): 002: 0> params = {: a => 1,: b => 2,: c => 3} => {: c => 3,: a => 1,: b => 2} irb (main): 003: 0>! Params [: name] .blank? NoMethodError: undefinierte Methode 'leer? ' für nil: NilClass von (irb): 3 –

+0

Kein Ruby hier, nur Rails. * deleted tag * – Nakilon

Antwort

25

Sie könnten versuchen, Active_form (http://github.com/cs/active_form/tree/master/lib/active_form.rb) - nur ActiveRecord minus die Datenbank Zeug. Auf diese Weise können Sie alle Validierungsdaten von AR verwenden und Ihr Formular wie jedes andere Modell behandeln.

class MyForm < ActiveForm 
    validates_presence_of :name 
    validates_presence_of :graph_size, :if => # ...blah blah 
end 

form = MyForm.new(params[:form]) 
form.validate 
form.errors 
+0

Ist 'validates_presence_of: name' ein Alias ​​für' validates: name, present: true'? Letzteres habe ich nur in Rails Guides gesehen. – Dennis

+1

Um meine eigene Frage zu beantworten, ist der letztgenannte Weg ein neuer Weg, die in Rails 3 eingeführten Validierungen zu machen. Http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/ – Dennis

6

Sieht aus wie Sie die Validierung im Controller tun, versuchen Sie es in dem Modell setzen, dann ist es besser geeignet, um diese Art der Sache.

+4

Wie auch im [guide] (http://guides.rubyonrails.org/active_record_validations_callbacks.html) empfohlen: "Validierungen auf Controller-Ebene können verlockend sein, werden aber oft unhandlich und schwer zu testen und zu testen Wenn es möglich ist, ist es eine gute Idee, die Controller dünn zu halten (http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model), da dies Ihre Anwendung sehr angenehm macht auf lange Sicht zu arbeiten. " –

2

Wenn Sie das Problem heute wieder in Angriff zu nehmen sind, Sie ein Modell für den Abfrage Parametersatz erstellen könnten und verwenden Rails' in Validierungen gebaut, Rails 3 macht dies viel einfacher mit ActiveModel :: Validierungen sehen this post.