Skip to content

[feature] Model Listing Page: column shouldn't be editable if column_formatters #2653

@princerb

Description

@princerb

For example, if Post model has a .body column (Text/String) which has a lot of data

and we set a formatter for it like this:

class PostModelView(ModelView):
    column_formatters = {
        "body": lambda v, c, m, p: m.body[:40] + "…" if len(m.body) > 40 else m.body,
    }

Then, we try to edit it on the model listing page, it will set and display the formatted value, due to this:

# flask_admin/model/base.py #L~2089
class BaseModelView(BaseView, ActionsMixin):
    def _get_list_value(...):
        column_fmt = column_formatters.get(name)
        if column_fmt is not None:
            value = column_fmt(self, context, model, name)
            #   ^^^^^^^^^^^^^^
        else:
            value = self._get_field_value(model, name)
        ...

As a result, /ajax/update/ will receive the updated version of the formatted value, not the actual value (Post.body).

[Screenshot] Image

♻️ Reproducable example:

from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SECRET_KEY'] = 'secret'
db = SQLAlchemy(app)

# Define a simple model
class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.Text)

# Custom ModelView with column formatter
class PostModelView(ModelView):
    column_list = ['body']
    column_editable_list = ['body']  # Enable inline editing
    column_formatters = {
        'body': lambda v, c, m, p: m.body[:40] + '...' if len(m.body) > 40 else m.body
    }

# Set up Flask-Admin
admin = Admin(app)
admin.add_view(PostModelView(Post, db.session))

# Create database and add a test record
with app.app_context():
    db.create_all()
    long_text = "This is a very long text that exceeds forty characters in length."
    db.session.add(Post(body=long_text))
    db.session.commit()

if __name__ == '__main__':
    app.run(debug=True)

Solution?

Possibly, we can either create a new endpoint for accessing a value of a single column unlike details_view, or raise an error - forbidding an editable feature on a formattable column.

or leave your own thoughts!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions