Validierung von Feldwerten
Formulardaten müssen validiert werden, denn jeder Userinput muss als schädlich betrachtet werden. Einen Großteil der technischen Validierung nimmt uns Django schon von Haus aus ab: wenn wir versuchen, ein Wort an ein Integerfeld zu übergeben, wird uns Django mit einem Validierungsfehler antworten. Wir könnten auch versuchen, ein Datum einzutragen, dass nicht als Datum geparst werden kann… das wird ebenfalls nicht gehen.
Oft müssen wir aber prüfen, ob die übergebenen Werte in Abhängigkeit unseren Anforderungen überhaupt plausibel sind. So mag ein Datum generell der richtige Datentyp sein, aber wenn es in der Vergangenheit liegt, ist es im Sinne des Event-Managers unsinnig.
Wir können, wir wir schon gesehen haben, die Eingabedaten in der Form-Klasse validieren. Das hat den Nachteil, dass die Validatoren, die dort definiert wurden, nicht in der Admin-Oberfläche oder in sonstigen Stellen berücksichtigt werden. Sie werden nur berücksichtigt, wenn Daten über ein Formular eingetragen werden.
Mehr zu Form-Validierung hier: https://docs.djangoproject.com/en/stable/forms/validation/
Deshalb ist es sinnvoll, Validatoren in den Models zu definieren.
Model Validatoren
Bei Model-Validatoren handelt es sich um Klassen oder Funktionen, die einzelnen
Feldern des Models zugeteilt werden. Dazu nutzen wir das validators
- Keyword Argument.
Eigene Validatoren entwickeln
Eigene Validatoren lassen sich einfach als Klasse oder Funktion entwickeln und den Model-Feldern zuordnen.
https://docs.djangoproject.com/en/stable/validators/#writing-validators
Validator-Funktion schreiben
In folgendem Beispiel erheben wir einen ValidationError
, wenn eine Zahl
nicht gerade ist:
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_even(value):
if value % 2 != 0:
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
Wir nutzen hier die gettext_lazy
- Funktion, um die Fehlermeldung später
auch mehrsprachig darstellen zu können.
Validator in Model nutzen:
Den Valdiator könnte man hypothetisch in einem Integerfield dann so nutzen:
from django.db import models
class MyModel(models.Model):
even_field = models.IntegerField(validators=[validate_even])
Built-In Validatoren nutzen
Django verfügt über eine große Anzahl an sogenannten built-in Validatoren, die
importiert und genutzt werden können. Zum Beispiel einen EmailValidator
oder einen MinValueValidator
Built in Validator nutzen
Im hypothetischen Beispiel nutzen wir MinValueValidator
und MaxValueValidator
, um die Körpergröße einzugrenzen.
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
class Person(models.Model):
height = models.IntegerField(
validators=[MinValueValidator(130), MaxValueValidator(220)]
)
Eine eigene Validation-Funktion erstellen
Wir wollen nun eigene Model-Felder validieren. Beim Eintragen der Events ist aufgefallen, dass es problemlos möglich ist, Daten, die in der Vergangenheit liegen, einzutragen. Das sollte nicht möglich sein. Deshalb wollen wir die Eingabe des date-Feldes des Event-Models validieren.
Wir erstellen eine neue Datei event_manager/events/validators.py
und erstellen dort die folgende Funktion:
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
def datetime_in_future(value):
"""prüfe, ob Zeitpunkt value in der Vergangenheit liegt.
wenn ja, raise ValidationError"""
if value <= timezone.now():
raise ValidationError(_("Der Zeitpunkt des Events darf nicht in der Vergangenheit liegen"))
Und importieren in den event_manager/events/models.py
dann den Validator:
from .validators import datetime_in_future
und weisen den Validator dem Modelfeld date
zu:
date = models.DateTimeField(validators=[datetime_in_future])
Dann führen wir makemigrations und migrate aus, da Validatoren auch migriert werden müssen.
python manage.py makemigrations events
python manage.py migrate events
Jetzt können wir unseren neuen Validator auch ausprobieren und versuchen ein Formular einzutragen, welches in der Vergangenheit liegt. Darauf achten, mit dem Superuser eingeloggt zu sein, damit der Eintrag auch klappt. Beim Absenden mit einem Datum in der Vergangenheit sollte das Feld rot hinterlegt sein, und einen Fehler anzeigen.
Der Form-Validierungsprozess
Formularfelder können also an mehreren Orten validiert werden. Django arbeitet die Validierung dabei in einem festen Prozess ab.
Dieser Validierungsprozess, der für jedes Feld durchgeführt wird, wird zum
Beispiel gestartet, wenn wir die Funktion is_valid()
aufrufen.
Der Prozess wird in der Django-Doku beschrieben, ist allerdings nicht sonderlich zugänglich: https://docs.djangoproject.com/en/stable/ref/forms/validation/
Hier nochmal eine vereinfachte Darstellung des Form-Validierungsprozesses, der genau in dieser Reihenfolge abgearbeitet wird:
|
wandelt Feldwerte in Python-Datentypen um und erhebt im |
Fall eines Fehlers einen ValidationError |
|
|
führt feldspezifische Validierungen durch und wirft einen |
ValidationError im Falle eines Fehlers |
|
|
führt die den Feldern zugewiesenen Validatoren aus |
und sammelt alle Fehler in einem |
|
|
führt neben |
aus und erstellt das |
|
|
ruft für jedes Feld mit FELDNAME und speichert den |
Rückgabewert in |
|
|
um feldübergreifende Validierungen vorzunehmen. |
Der Rückgabewert ist das finale |
https://docs.djangoproject.com/en/stable/validators/#built-in-validators