Directly marking fields as “read-only” within SQLAlchemy ORM models is not supported, as ORM models primarily define data schema and persistence logic, not UI behavior. To achieve read-only fields in an SQLAlchemy-based admin interface, such as Flask-Admin, configure the specific ModelView to adjust form rendering.

The Problem

Developers building administrative interfaces with SQLAlchemy often require certain fields to be visible in both view and edit modes but non-editable. This is common for fields automatically populated by database triggers, external processes, or default values (e.g., created_at, external_id). Attempts to set a “ReadOnly” attribute directly within a SQLAlchemy ORM model definition will result in an error because such functionality belongs to the user interface layer, not the data model.

The Solution

To display a field in an admin interface’s edit form while preventing user modification, configure the form_args attribute within your ModelView class. This allows passing HTML attributes like disabled or readonly to the field’s input widget.

from flask_admin.contrib.sqla import ModelView
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
from sqlalchemy.ext.declarative import declarative_base

# Define your SQLAlchemy model
Base = declarative_base()

class Item(Base):
    __tablename__ = 'items'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)
    # This field is assumed to be set by other processes and should be read-only
    external_reference_id = Column(String(50), unique=True, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    def __repr__(self):
        return f'<Item {self.name}>'

# Configure your Flask-Admin ModelView
class ItemAdminView(ModelView):
    # Use form_args to apply rendering properties to specific fields in the form.
    # Setting 'disabled': True makes the field uneditable and prevents its value
    # from being submitted during form updates, ideal for auto-managed fields.
    form_args = {
        'external_reference_id': {
            'render_kw': {'disabled': True}
        },
        'created_at': {
            'render_kw': {'disabled': True}
        }
    }

    # To integrate this, add the view to your Flask-Admin instance:
    # from flask_admin import Admin
    # from flask_sqlalchemy import SQLAlchemy
    # app = Flask(__name__)
    # db = SQLAlchemy(app) # assuming your SQLAlchemy engine and session are set up
    # admin = Admin(app, name='My Admin')
    # admin.add_view(ItemAdminView(Item, db.session))

Why It Works

  • Separation of Concerns: SQLAlchemy ORM models are designed to map Python objects to database tables and manage persistence. User interface presentation logic, including field editability, is intentionally decoupled and handled by the administrative framework (e.g., Flask-Admin).
  • Flask-Admin’s ModelView Configuration: Flask-Admin’s ModelView provides form_args as a dictionary to pass arguments directly to the underlying WTForms fields used for the edit and create forms.
  • HTML disabled Attribute: By providing render_kw={'disabled': True} for a field within form_args, Flask-Admin renders the corresponding HTML input element with the disabled attribute. A disabled input field is visible but cannot be edited by the user and its value is typically not submitted with the form, which is appropriate for fields managed entirely by the backend or database. If the field’s value needs to be submitted but not editable by the user, render_kw={'readonly': True} can be used instead.

Reference