2009-10-26 11 views
53

Wo sollte die Validierung von Modellfelder in Django gehen?Django Modellfelder Validierung

Ich könnte mindestens zwei mögliche Alternativen nennen: in der überladenen .save() - Methode des Modells oder in der .to_python() -Methode der models.Field-Unterklasse (offensichtlich dafür müssen Sie benutzerdefinierte Felder schreiben) .

Mögliche Anwendungsfälle:

  • , wenn es unbedingt erforderlich, um sicherzustellen, dass ein leerer String nicht in die Datenbank geschrieben werden (blank = False Schlüsselwort-Argument hier nicht funktioniert, ist es für Form Validierung nur)
  • , wenn es notwendig ist, um sicherzustellen, dass „Entscheidungen“ Keyword-Argument auf einer db-Ebene eingehalten wird und nicht nur in der Admin-Interface (Art einen Aufzählungsdatentyp emuliert)

es gibt auch eine Attribut auf Klassenebene empty_strings_allowed in der models.Field-Basisklassendefinition und abgeleiteten Klassen überschreiben diese glücklicherweise, aber es scheint keine Auswirkungen auf die Datenbankebene zu haben, was bedeutet, dass ich immer noch ein Modell mit leeren String-Feldern konstruieren und es in der Datenbank speichern kann. Was ich vermeiden möchte (ja, es ist notwendig).

Mögliche Implementierungen

auf der Feldebene sind:

class CustomField(models.CharField): 
    __metaclass__ = models.SubfieldBase 
    def to_python(self, value): 
     if not value: 
      raise IntegrityError(_('Empty string not allowed')) 
     return models.CharField.to_python(self, value) 

auf Modellebene:

class MyModel(models.Model) 
    FIELD1_CHOICES = ['foo', 'bar', 'baz'] 
    field1 = models.CharField(max_length=255, 
       choices=[(item,item) for item in FIELD1_CHOICES]) 

    def save(self, force_insert=False, force_update=False): 
     if self.field1 not in MyModel.FIELD1_CHOICES: 
      raise IntegrityError(_('Invalid value of field1')) 
     # this can, of course, be made more generic 
     models.Model.save(self, force_insert, force_update) 

Vielleicht bin ich etwas fehlt und dies einfacher durchgeführt werden kann (und sauberer)

Antwort

55

Django hat ein model validation System seit Version 1.2.

In den Kommentaren sagt sebpiq: "Ok, jetzt gibt es einen Platz für die Modellvalidierung ... außer, dass es nur bei Verwendung von ModelForm ausgeführt wird! Es bleibt also die Frage, wann die Validierung eingehalten werden muss das db-level, was solltest du tun? wo full_clean anrufen? "

Es ist nicht möglich, über die Validierung auf Python-Ebene sicherzustellen, dass die Validierung auf der Ebene der Datenbank eingehalten wird. Am nächsten kommt wahrscheinlich full_clean in einer überschriebenen save Methode. Dies wird nicht standardmäßig ausgeführt, da jeder, der diese Speichermethode aufruft, jetzt besser darauf vorbereitet ist, ValidationError zu erfassen und zu verarbeiten.

Aber selbst wenn Sie dies tun, kann jemand weiterhin Modellinstanzen in großen Mengen unter Verwendung von queryset.update() aktualisieren, wodurch diese Validierung umgangen wird.Es gibt keine Möglichkeit, dass Django eine vernünftig effiziente queryset.update() Implementierung implementieren könnte, die immer noch eine Überprüfung auf Python-Ebene für jedes aktualisierte Objekt durchführen könnte.

Die einzige Möglichkeit, die Integrität auf Db-Ebene wirklich zu garantieren, sind die Constraints auf Datenbankebene; Jede Validierung, die Sie über das ORM durchführen, erfordert, dass der Verfasser des App-Codes erkennt, wann die Validierung erzwungen wird (und Validierungsfehler behandeln).

Aus diesem Grund wird die Modellüberprüfung standardmäßig nur in ModelForm erzwungen - da in einem ModelForm bereits eine offensichtliche Möglichkeit besteht, einen ValidationError zu behandeln.

+0

Großartig, großartig, das ist einfach großartig! Ich habe die Quelle überflogen (übrigens, gibt es eine Möglichkeit, auf die Dokumentation zuzugreifen, wie es mit dem Trunk möglich ist?) Und es scheint genau das zu sein, was ich brauche. Ich meine, ich bin dafür, mein eigenes zu rollen, aber Django ist _excellent_ bei der Bereitstellung einer einheitlichen Art und Weise, Dinge zu tun (na ja, IMO, jedenfalls). – shylent

+2

Überprüfen Sie diesen Zweig, stellen Sie sicher, dass Sie docutils und Sphinx installiert haben, gehen Sie dann in das Verzeichnis docs/und führen Sie "make html" aus. Das sollte die Dokumente in HTML-Form wie auf der Django-Website erstellen, und Sie können lokal auf sie zugreifen. –

+0

Ok, ich bin zurück vom Lesen der Quelle (insbesondere 'models/fields/__ init __. Py', models/base.py und core/validators.py), da die Dokumentation ab sofort nichts über die Modellvalidierung sagt. Man sollte jedoch beachten, dass es fast genauso funktioniert wie die Validierung von Formularen (zumindest ist die allgemeine Logik mehr oder weniger die gleiche). Wie auch immer, das ist etwas, wonach ich gesucht habe. Ich hoffe nur, dass meine Apps nicht schrecklich kaputt gehen werden, wenn ich nur vom Stamm in diesen Zweig umschalte. – shylent

1

Wenn ich Sie „klar“ zu verstehen - Sie müssen Funktion get_db_prep_save außer Kraft setzen statt to_python

+0

Das ist eigentlich eine gute Idee. Ich habe jedoch ausdrücklich .to_python() erwähnt, weil es ausgelöst wird, sobald das Feld initialisiert ist (wenn Sie __metaclass__ = models.SubfieldBase angeben), damit die Validierung früh erfolgt, dh Sie können ein Modell nicht einmal initialisieren, wenn Sie es übergeben schlechte Werte für die Felder. Vielleicht ist dein Weg der richtige Weg. Zumindest macht es mir einen Sinn. – shylent

+0

so überschreiben beide – Oduvan

3

Die Wurzel Problem dafür ist, dass die Validierung von Modellen geschehen soll. Dies wird seit einiger Zeit in Django diskutiert (Suche Modell-bewusste Validierung auf der Dev-Mailing-Liste). Es führt entweder zu Duplizierung oder Dingen, die der Validierung entgehen, bevor sie auf die db treffen.

Während das nicht Kofferraum schlägt, ist Malcolms "poor man's model validation solution" wahrscheinlich die sauberste Lösung, sich zu vermeiden, sich zu wiederholen.

+1

Dieser Link ist für mich gebrochen ... –

+0

Ich habe den Inhalt davon im Google Cache gelesen. Es macht Sinn, ja. Es ist nicht sehr hilfreich, wenn ich keine Formulare verwenden werde (brauche keine Formulare, um Daten einzugeben, oder?), Aber es ist sicherlich eine Möglichkeit, mich nicht zu wiederholen. – shylent

6

Ich glaube, Sie wollen, dass diese ->

from django.db.models.signals import pre_save 

def validate_model(sender, **kwargs): 
    if 'raw' in kwargs and not kwargs['raw']: 
     kwargs['instance'].full_clean() 

pre_save.connect(validate_model, dispatch_uid='validate_models') 

(Kopiert von http://djangosnippets.org/snippets/2319/)

+0

Nein, nicht wirklich. Modellvalidierung ist im Moment in Django stabil, also ist der Punkt der ursprünglichen Frage irgendwie strittig – shylent

+2

Ich glaube nicht, weil Validatoren nicht laufen, wenn Sie speichern, damit es Sie Sachen hinzufügen lässt, die nicht tun bestätigen. Von http://docs.djangoproject.com/en/dev/releases/1.2/#model-validation "Einfaches Aufrufen der save() - Methode einer Modellinstanz führt keine Überprüfung der Daten der Instanz durch." Obwohl ich vielleicht missverstanden habe, was OP will, aber * ich * würde sicherlich eine Möglichkeit haben, save() zu validieren :) – Darius

Verwandte Themen