I have been intending to create a custom form validation logic that's based on multiple field values (in my case, 2 fields to ensure date range data integrity is ensured, I.E. start_time < end_time). However, flipping through the documentation on flask admin site, I couldn't find anywhere to do this sort of stuff. I am aware of that you can pretty trivially pass a list of validation functions to validators
argument for form_args
property in a subclass of a BaseModelView
class, but again, that's per-field validation not exactly what I want.
So my question is this: how does one validate multiple fields together at once?
Also, I don't see any pre-save hook event function to tap into do do this. I am aware of on_model_change
but then it's a post-save hook, it would defeat the purpose of validation to put the validation in there. What would be the appropriate the way go about doing pre-save actions?
So after experimenting and trying out a few different approaches, the way I did multiple form field validation is still to peg along on_model_change
I know it says the event hook is called after the changes have been made - however, since it's been wrapped in a transaction, one can raise any exception to roll back the transaction.
Here is my sample code to make it work.
from flask.ext.admin.form import rules
from wtforms import validators
class TimeWindowView(LoggedInView):
column_filters = ('scheduled_start', 'scheduled_end')
form_create_rules = [
rules.Field('scheduled_start'),
rules.Field('scheduled_end'),
]
def on_model_change(self, form, model, is_created):
# if end date before start date or end date in the past, flag them invalid
if (form.scheduled_end.data <= form.scheduled_start.data or
form.scheduled_end.data <= datetime.datetime.utcnow()):
raise validators.ValidationError('Invalid schedule start and end time!')
else:
super().on_model_change(form, model, is_created)
You can introduce custom form validation code to your flask admin model views by using inheritance and a custom "validate_form" method that incorporates your validation code before calling the parent "validate form".
If your validation logic finds a problem, your validate_form should display an appropriate error message and return False, otherwise it should continue by running the original flask admin validation form code.
from flask_admin.contrib.sqla import ModelView
from flask import flash
class MyModelView(ModelView):
""" My model admin model view """
def validate_form(self, form):
""" Custom validation code that checks dates """
if form.start_time.data > form.end_time.data:
flash("start time cannot be greater than end time!")
return False
return super(MyModelView, self).validate_form(form)
This is a much more natural place to do form validation than at the change_model_model method, which shouldn't be concerned with model logic, not form validation logic. Also, note we don't need to use an exception and rely on reversing the transaction. We simply catch the error before any transaction has occurred, flash an error message and gracefully return a "False" state.
Relevant links:
flask admin documentation
flask admin source code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With