Flask-Admin ModelView custom validation?
Asked Answered
L

1

11

I'm studying Flask-Admin combined with PeeWee Backend ModelView (but my question may be applied to SQLAlchemy backend too), and there are two things I could not find in the docs or examples:

(1). When I my model has an unique field and I test/try to duplicate it, I get a default Flask crash screen, with the message: "IntegrityError: column username is not unique"

I'm testing the PeeWee example available in https://github.com/mrjoes/flask-admin/blob/master/examples/peewee/simple.py, and I changed line 21 to "username = peewee.CharField(max_length=80, unique=True)"

Then I try to add two users with "username" = "user1".

Is there a polite way to get back to the edit screen (or even the list screen, any admin screen would do) but with a controlled error message? I don't need a custom error message, current message is ok (IntegrityError: column username is not unique). But I don't wan't the crash screen.

I could setup/use Flask's default 500 page, but then I would exit completelly the Flask-Admin flow and the user would "miss" the data he just typed.

I would like to get back to the edit screen, but with some sort of alert/error message. I don't mind to have to extend the templates, this is not a problem. But I could not find a place to intercept the error and handle it properly. Any suggestions?

and (2):

I also need a way to add some pre-save validation in the flow. For example, I'm in an edit form of an entity that has initial_date and final_date, and I want to make sure final_date is greater than initial_date or is null, before save.

I could do this client-side, via javascript, extending the edit template for that entity and adding my validation script in the tail block (and intercepting the form.submit event).

But what if my validation demands some server-side last-minute validation? Is there any way / place to intercept the flow and add my validation, and with luck, throw back my error message, in the same fashion discussed in question 1?

thanks in advance,

regards,

Lusatian answered 8/4, 2014 at 20:5 Comment(3)
Reason why it crashes - Flask-Admin will rethrow all exceptions (even IntegrityError) when Flask is running in debug mode. I'm going to add IntegrityError handling as an exception to this rule. pre-save can be done in the form or in on_model_change - you can throw exception from there. While exception will be shown with Werkzeug debugger, in production mode you'll see nice error message.Or
@Or on_model_change is a post-save hook as noted in documentation": "Perform some actions after a model is created or updated." There is no pre-save hook in Flask admin AFAIK. So extending the base form class and customization in sub class is the only way to answer OP's question.Teel
@Teel on_model_change is called when model was updated with form data, but before session was committed to the database. So throwing exception there would prevent any changes to be applied.Or
L
23

I guess I've found some things that help, but don't completelly answer my question.

I posted an example on PasteBin: http://pastebin.com/siwiaJAw

first, I could not find a 'before save' step, but I've found how to add field-level custom validation, which helps in the cases of creates and updates.

If you check the example, you'll see lines 37-39 and 42-44 where I added:

def no_root_allowed(form, field):
    if field.data == 'root':
        raise ValidationError('"root" is not allowed')

form_args = dict(
    username=dict(validators=[no_root_allowed])
)

This validator receives 'form' and 'field' and so I can do validations that involve more than one field (like start date < finish date), and I can probably even access the Model classes to check for id duplication, for example.

So, this solves the create/edit validation. If the validator fails, Flask-Admin drives me back to the edit form, already populated. Nice!

But there is a problem with deletions. If I don't want a specific object to be deleted, I can flag it as can_delete = False, but that would prevent any instance from being deleted.

The best I could find was to intercept the delete_model method (line 55) and return False in case of a pre-delete-validation fail. But that would not raise any message, and would just drive me back to the form. I could find no way to send a message to the user that he could not delete that particular instance.

I'll keep studying and will post any further news.

thanks!

Lusatian answered 14/4, 2014 at 19:44 Comment(3)
Thanks a TON for posting this. Saved me lots of time digging around.Marilyn
do you know if it is possible to for a custom validator to tell whether it's updating an existing model, or creating a new one? Something like is_created attribute?Marilyn
You can use flash('delete not allowed') to inform the user that delete is not available. from flask import flashHandtohand

© 2022 - 2024 — McMap. All rights reserved.