flask_admin change inline_models behaviour

flask, flask-admin, python

Solution

Yeesh. It looks like we're out in poorly-documented territory, here. It's hard to know if I'm improving on what you've already found, but I'll hope you're looking for something easier than writing a custom administrative view template.

Following the calls, it looks like the options dictionary eventually gets passed to the constructor of InlineBaseFormAdmin where the various form_* keys are extracted and applied (not sure all are respected, but I see at least `form_base_class, form_columns, form_excluded_columns, form_args, form_extra_fields, form_rules, form_label, form_column_labels, form_widget_args`). I think you can accomplish what you need via `form_widget_args`, but you can probably also get there via `form_rules` or by overriding InlineBaseFormAdmin's `get_form` or `postprocess_form` methods:

class SomeModelView(MyBaseModelView):
    ...
    inline_models = [(db.SomeOtherModel, {
        "form_widget_args": {
            "uneditable_field_name": {"readonly": True}
        }
    })]
    ...

The delete option can be controlled by providing your own inline form model to override `display_row_controls`:

from flask_admin.contrib.sqla.form import InlineModelConverter
from flask_admin.contrib.sqla.fields import InlineModelFormList

class CrouchingTigerHiddenModelFormList(InlineModelFormList):
    def display_row_controls(self, field): return False

class MyInlineModelConverter(InlineModelConverter):
    inline_field_list_type = CustomInlineModelFormList

#adding to above example
class SomeModelView(MyBaseModelView):
    ...
    inline_model_form_converter = MyInlineModelConverter
    inline_models = [(db.SomeOtherModel, {
        "form_widget_args": {
            "uneditable_field_name": {"readonly": True}
        }
    })]
    ...

NOTE: The widget args, such as readonly, are getting passed on to wtforms as render_kw, but at a blush the WTForms docs aren't clear that these get expressed as attributes in the resulting HTML input element (so any HTML input element attributes are valid here).

It looks like form.js controls this behavior, so you should be able to monkey-patch its `addInlineField` method to execute your own code before or after the model addition. You could override the create and/or edit templates for this--but if you're using flask-admin 1.5.0+, this might be as simple as adding `extra_js = ["your-custom.js"]` to the view class (caution: it looks like this script gets included on every page for this view).

Problem

I want to change some existing Python code that uses `flask_admin`. One of the views uses `inline_models` with the `(ClassName, Options)` declaration pattern. The inlined class has, amongst others, a text field. I want to change the flask_admin default behaviour in the following ways: - I want to make the text field read-only. I.e. still display it, but prevent the user from changing existing content. - I do not want to allow users to delete instances of the inlined class, i.e. I want to get rid of the "Delete?" checkbox next to every entry. - I want to override the default "Add Item" button behaviour with some custom JavaScript. I did some Googling around but anything that looked potentially promising also looked very non-trivial. I'm hoping for some reasonably straight forward way to achieve this. Your help would be much appreciated.

Original source