diff --git a/README.md b/README.md index 8ecf45a..0ab31ff 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,48 @@ to the second element in the schema, and succeed: ``` +## Multi-field validation + +Validation rules that involve multiple fields can be implemented as +custom validators. It's recommended to use `All()` to do a two-pass +validation - the first pass checking the basic structure of the data, +and only after that, the second pass applying your cross-field +validator: + +```python +def passwords_must_match(passwords): + if passwords['password'] != passwords['password_again']: + raise Invalid('passwords must match') + return passwords + +s=Schema(All( + # First "pass" for field types + {'password':str, 'password_again':str}, + # Follow up the first "pass" with your multi-field rules + passwords_must_match +)) + +# valid +s({'password':'123', 'password_again':'123'}) + +# raises MultipleInvalid: passwords must match +s({'password':'123', 'password_again':'and now for something completely different'}) + +``` + +With this structure, your multi-field validator will run with +pre-validated data from the first "pass" and so will not have to do +its own type checking on its inputs. + +The flipside is that if the first "pass" of validation fails, your +cross-field validator will not run: + +``` +# raises Invalid because password_again is not a string +# passwords_must_match() will not run because first-pass validation already failed +s({'password':'123', 'password_again': 1337}) +``` + ## Running tests. Voluptuous is using nosetests: